Skip to content

Project

Python style interface for arcpy.mp submodule

This module attempts to wrap the mp classes and functions in a way that provides a more OO approach to navigating and managing ArcGISProject objects and their associated elements.

All list* methods are replaced with ElementList properties that allow indexing on longName/name/URI/regex name match.

All elements when accessed from a Project root will have a parent attribute that allows traversal of the CIM/DOM

Objects that returned | None have been mostly replaced with Exceptions. Anywhere that this has been done, a boolean property has been included to allow checking the property before accessing it. If you expect the property to exist, allow the Exception to be raised and handle that properly in your implementation.

Overhead has been kept as low as possible. There is agressive cache usage, so manual invalidation using Element.refresh is required if you modify an element outside this framework.

Some elements have no CIM properties in arcpy, but the aprx file actually contains their raw CIM definition. These elements allow you to access the CIM in read-only mode and will not allow you to set it.

Example
>>> with Project('my-project.aprx') as prj:
...     for map in prj.maps['Plan*']:
...         for lay in map.layers['*Route']:
...             # when using `with Project() as ...`
...             # Project.save is called on __exit__
...             if lay.name == 'Proposed Route':
...                 lay.visible = False
...             print(f'{lay.name} (2024+): {len(lay)}')
...             with lay.query_as('YEAR >= 2024'):
...                 print(f'{lay.name} (2024+): {len(lay)}')
Proposed Route: 216
Proposed Route (2024+): 65
Final Route: 673
Final Route (2024+): 175
CLASS DESCRIPTION
Project

ArcGISProject wrapper

ChartType module-attribute

ChartType = Literal[
    'Bar',
    'Box',
    'CalendarHeat',
    'Combo',
    'DataClock',
    'Histogram',
    'Line',
    'MatrixHeat',
    'Pie',
    'QQPlot',
    'Scatter',
    'ScatterMatrix',
]

ChartTypes module-attribute

ChartTypes: tuple[ChartType, ...] = ChartType.__args__

LayerProperty module-attribute

LayerProperty = Literal[
    'brightness',
    'connectionProperties',
    'contrast',
    'dataSource',
    'definitionQuery',
    'elevation',
    'is3DLayer',
    'isBasemapLayer',
    'isBroken',
    'isFeatureLayer',
    'isGraphicsLayer',
    'isGroupLayer',
    'isNetworkAnalystLayer',
    'isNetworkDatasetLayer',
    'isParcelFabricLayer',
    'isRasterLayer',
    'isSceneLayer',
    'isTimeEnabled',
    'isWebLayer',
    'longName',
    'maxThreshold',
    'metadata',
    'minThreshold',
    'name',
    'showLabels',
    'symbology',
    'transparency',
    'time',
    'URI',
    'visible',
]

LayerProps module-attribute

LayerProps = frozenset[LayerProperty](
    LayerProperty.__args__
)

LayerType module-attribute

LayerType = Literal[
    '3d',
    'basemap',
    'broken',
    'feature',
    'graphics',
    'group',
    'network-analyst',
    'network-dataset',
    'parcel-fabric',
    'raster',
    'scene',
    'time-enabled',
    'topology',
    'web',
    'annotation-layer',
    'annotation-sublayer',
    'dimension-layer',
    'terrain-layer',
    'raster-catalog-layer',
]

LayerTypes module-attribute

LayerTypes: tuple[LayerType, ...] = LayerType.__args__

LegendFittingStrategy module-attribute

LegendFittingStrategy = Literal[
    'AdjustFontSize',
    'AdjustColumns',
    'AdjustColumnsAndFont',
    'AdjustFrame',
    'ManualColumns',
]

LegendItemArrangement module-attribute

LegendItemArrangement = Literal[
    'PatchLabelDescription',
    'PatchDescriptionLabel',
    'LabelPatchDescription',
    'LabelDescriptionPatch',
    'DescriptionPatchLabel',
    'DescriptionLabelPatch',
]

NotSupported module-attribute

NotSupported = _PropNotSupported()

StatisticType module-attribute

StatisticType = Literal[
    'COUNT',
    'MEAN',
    'MEDIAN',
    'SUM',
    'STD_DEV',
    'MAX',
    'MIN',
]

__all__ module-attribute

__all__ = ('Project',)

root module-attribute

root = str(Path(__file__).parent.parent.parent.resolve())

BookmarkLike

BookmarkLike = Bookmark | Bookmark

BookmarkMapSeriesLike

BookmarkMapSeriesLike = (
    BookmarkMapSeries | BookmarkMapSeries
)

Chart

Chart = (
    Bar
    | Box
    | CalendarHeat
    | Combo
    | DataClock
    | Histogram
    | Line
    | MatrixHeat
    | Pie
    | QQPlot
    | Scatter
    | ScatterMatrix
)

ElevationSourceLike

ElevationSourceLike = ElevationSource | ElevationSource

ElevationSurfaceLike

ElevationSurfaceLike = ElevationSurface | ElevationSurface

GraphicElementLike

GraphicElementLike = GraphicElement | GraphicElement

GroupElementLike

GroupElementLike = GroupElement | GroupElement

GroupLayerLike

GroupLayerLike = GroupLayer | Layer

LabelClassLike

LabelClassLike = LabelClass | LabelClass

LayerLike

LayerLike = Layer | Layer

LayoutLike

LayoutLike = Layout | Layout

LegendElementLike

LegendElementLike = LegendElement | LegendElement

LegendItemLike

LegendItemLike = LegendItem | LegendItem

MPElement

MPElement = (
    ArcGISProject
    | Map
    | MapView
    | Layer
    | Table
    | ElevationSurface
    | Layout
    | MapFrame
    | MapSeries
    | Bookmark
    | BookmarkMapSeries
    | Report
    | ElevationSource
    | StyleItem
    | LabelClass
    | MapSurroundElement
    | TableFrameElement
    | GraphicElement
    | GroupElement
    | LegendElement
    | LegendItem
    | PictureElement
    | TextElement
    | ReportSection
    | ReportLayoutSection
    | None
)

MapFrameLike

MapFrameLike = MapFrame | MapFrame

MapLike

MapLike = Map | Map

MapSeriesLike

MapSeriesLike = MapSeries | MapSeries

MapSurroundElementLike

MapSurroundElementLike = (
    MapSurroundElement | MapSurroundElement
)

MapViewLike

MapViewLike = MapView | MapView

PictureElementLike

PictureElementLike = PictureElement | PictureElement

ReportLayoutSectionLike

ReportLayoutSectionLike = (
    ReportLayoutSection | ReportLayoutSection
)

ReportLike

ReportLike = Report | Report

ReportSectionLike

ReportSectionLike = ReportSection | ReportSection

StyleItemLike

StyleItemLike = StyleItem | StyleItem

TableFrameElementLike

TableFrameElementLike = (
    TableFrameElement | TableFrameElement
)

TableLike

TableLike = Table | Table

TextElementLike

TextElementLike = TextElement | TextElement

APRXNode

Bases: TypedDict

ATTRIBUTE DESCRIPTION
ChildNodeIds

TYPE: str

FileName

TYPE: str

NodeId

TYPE: int

NodeType

TYPE: str

ChildNodeIds instance-attribute

ChildNodeIds: str

FileName instance-attribute

FileName: str

NodeId instance-attribute

NodeId: int

NodeType instance-attribute

NodeType: str

AllLayerProps

Bases: TypedDict

ATTRIBUTE DESCRIPTION
URI

TYPE: str

brightness

TYPE: int

connectionProperties

TYPE: LayerConnectionConf

contrast

TYPE: int

dataSource

TYPE: str

definitionQuery

TYPE: str

elevation

TYPE: LayerElevation

is3DLayer

TYPE: bool

isBasemapLayer

TYPE: bool

isBroken

TYPE: bool

isFeatureLayer

TYPE: bool

isGraphicsLayer

TYPE: bool

isGroupLayer

TYPE: bool

isNetworkAnalystLayer

TYPE: bool

isNetworkDatasetLayer

TYPE: bool

isParcelFabricLayer

TYPE: bool

isRasterLayer

TYPE: bool

isSceneLayer

TYPE: bool

isTimeEnabled

TYPE: bool

isWebLayer

TYPE: bool

longName

TYPE: str

maxThreshold

TYPE: float

metadata

TYPE: Metadata

minThreshold

TYPE: float

name

TYPE: str

showLabels

TYPE: bool

symbology

TYPE: Symbology

time

TYPE: LayerTime

transparency

TYPE: int

visible

TYPE: bool

URI instance-attribute

URI: str

brightness instance-attribute

brightness: int

connectionProperties instance-attribute

connectionProperties: LayerConnectionConf

contrast instance-attribute

contrast: int

dataSource instance-attribute

dataSource: str

definitionQuery instance-attribute

definitionQuery: str

elevation instance-attribute

elevation: LayerElevation

is3DLayer instance-attribute

is3DLayer: bool

isBasemapLayer instance-attribute

isBasemapLayer: bool

isBroken instance-attribute

isBroken: bool

isFeatureLayer instance-attribute

isFeatureLayer: bool

isGraphicsLayer instance-attribute

isGraphicsLayer: bool

isGroupLayer instance-attribute

isGroupLayer: bool

isNetworkAnalystLayer instance-attribute

isNetworkAnalystLayer: bool

isNetworkDatasetLayer instance-attribute

isNetworkDatasetLayer: bool

isParcelFabricLayer instance-attribute

isParcelFabricLayer: bool

isRasterLayer instance-attribute

isRasterLayer: bool

isSceneLayer instance-attribute

isSceneLayer: bool

isTimeEnabled instance-attribute

isTimeEnabled: bool

isWebLayer instance-attribute

isWebLayer: bool

longName instance-attribute

longName: str

maxThreshold instance-attribute

maxThreshold: float

metadata instance-attribute

metadata: Metadata

minThreshold instance-attribute

minThreshold: float

name instance-attribute

name: str

showLabels instance-attribute

showLabels: bool

symbology instance-attribute

symbology: Symbology

time instance-attribute

time: LayerTime

transparency instance-attribute

transparency: int

visible instance-attribute

visible: bool

Bookmark

Bookmark(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Bookmark, CIMBookmark, Map]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

update_thumbnail

Update the thumbnail for the Bookmark.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

description

The description of the Bookmark.

TYPE: str

elem

has_map

Determine if the Bookmark has an associated Map.

TYPE: bool

has_thumbnail

Determine if the Bookmark has a thubmnail.

TYPE: bool

map

The Map the Bookmark is associated with. (If not built by arcpie, Map parent will be None)

TYPE: Map

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

description property writable

description: str

The description of the Bookmark.

elem instance-attribute

elem = cast(MPElem, elem)

has_map property

has_map: bool

Determine if the Bookmark has an associated Map.

has_thumbnail property

has_thumbnail: bool

Determine if the Bookmark has a thubmnail.

map property

map: Map

The Map the Bookmark is associated with. (If not built by arcpie, Map parent will be None)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

update_thumbnail

update_thumbnail() -> None

Update the thumbnail for the Bookmark.

Source code in src/arcpie/project/elements.py
4322
4323
4324
def update_thumbnail(self) -> None:
    """Update the thumbnail for the Bookmark."""
    self.elem.updateThumbnail()

BookmarkMapSeries

BookmarkMapSeries(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[BookmarkMapSeries, CIMBookmarkMapSeries, Layout]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__len__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

reload

Reload the BookmarkMapseries. Alias of refresh since refresh is used for cache.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
bookmarks

ElementList of all associated Bookmarks.

TYPE: ElementList[Bookmark]

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

current_bookmark

Get the current Bookmark.

TYPE: Bookmark

current_page_name

Get the current Bookmark name.

TYPE: str

current_page_number

Get the current page number.

TYPE: int

elem

has_current_bookmark

Determine if there is a current Bookmark set.

TYPE: bool

has_map_frame

Determine if the BookmarkMapSeries has an associated MapFrame.

TYPE: bool

map_frame

The Associated MapFrame for the BookmarkMapSeries. (raises: AttributeError)

TYPE: MapFrame

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

page_count

Total number of pages in the BookmarkMapSeries.

TYPE: int

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

bookmarks property

bookmarks: ElementList[Bookmark]

ElementList of all associated Bookmarks.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

current_bookmark property

current_bookmark: Bookmark

Get the current Bookmark.

current_page_name property writable

current_page_name: str

Get the current Bookmark name.

current_page_number property writable

current_page_number: int

Get the current page number.

elem instance-attribute

elem = cast(MPElem, elem)

has_current_bookmark property

has_current_bookmark: bool

Determine if there is a current Bookmark set.

has_map_frame property

has_map_frame: bool

Determine if the BookmarkMapSeries has an associated MapFrame.

map_frame property

map_frame: MapFrame

The Associated MapFrame for the BookmarkMapSeries. (raises: AttributeError)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

page_count property

page_count: int

Total number of pages in the BookmarkMapSeries.

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__len__

__len__() -> int
Source code in src/arcpie/project/elements.py
4334
4335
def __len__(self) -> int:
    return self.page_count

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

reload

reload() -> None

Reload the BookmarkMapseries. Alias of refresh since refresh is used for cache.

Source code in src/arcpie/project/elements.py
4394
4395
4396
def reload(self) -> None:
    """Reload the BookmarkMapseries. Alias of `refresh` since `refresh` is used for cache."""
    self.elem.refresh()

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ChartMap

Bases: TypedDict

ATTRIBUTE DESCRIPTION
Bar

TYPE: list[Bar]

Box

TYPE: list[Box]

CalendarHeat

TYPE: list[CalendarHeat]

Combo

TYPE: list[Combo]

DataClock

TYPE: list[DataClock]

Histogram

TYPE: list[Histogram]

Line

TYPE: list[Line]

MatrixHeat

TYPE: list[MatrixHeat]

Pie

TYPE: list[Pie]

QQPlot

TYPE: list[QQPlot]

Scatter

TYPE: list[Scatter]

ScatterMatrix

TYPE: list[ScatterMatrix]

Bar instance-attribute

Bar: list[Bar]

Box instance-attribute

Box: list[Box]

CalendarHeat instance-attribute

CalendarHeat: list[CalendarHeat]

Combo instance-attribute

Combo: list[Combo]

DataClock instance-attribute

DataClock: list[DataClock]

Histogram instance-attribute

Histogram: list[Histogram]

Line instance-attribute

Line: list[Line]

MatrixHeat instance-attribute

MatrixHeat: list[MatrixHeat]

Pie instance-attribute

Pie: list[Pie]

QQPlot instance-attribute

QQPlot: list[QQPlot]

Scatter instance-attribute

Scatter: list[Scatter]

ScatterMatrix instance-attribute

ScatterMatrix: list[ScatterMatrix]

DefinitionQuery

Bases: TypedDict

ATTRIBUTE DESCRIPTION
isActive

TYPE: bool

name

TYPE: str

sql

TYPE: str

isActive instance-attribute

isActive: bool

name instance-attribute

name: str

sql instance-attribute

sql: str

Element

Element(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)
METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ElementList

Bases: list[E]

Simple list wrapper that allows accessing elements from a list by name/uRI.

METHOD DESCRIPTION
__add__
__contains__
__getitem__
copy
filter

Filter elements in the list using the provided function

get

Get the item from the list but return the default if it doesn't exist

unwrap

Unwrap the ElementList and check to see if it has the expected number of items

__add__

__add__(value: Sequence[E]) -> Self
__add__(value: Sequence[S]) -> ElementList[S | E]
__add__(
    value: Sequence[E] | Sequence[S],
) -> ElementList[S | E] | ElementList[E]
Source code in src/arcpie/project/elements.py
594
595
def __add__[S: Element[Any, Any, Any]](self, value: Sequence[E] | Sequence[S], /) -> ElementList[S | E] | ElementList[E]:  # type: ignore (we don't want non-Elements)
    return ElementList(super().__add__(list(value)))

__contains__

__contains__(key: Any) -> bool
Source code in src/arcpie/project/elements.py
578
579
580
581
582
583
584
def __contains__(self, key: Any) -> bool:
    if super().__contains__(key):
        return True
    try:
        return self.get(key) is not None
    except Exception:
        return False

__getitem__

__getitem__(i: SupportsIndex) -> E
__getitem__(s: slice) -> Self
__getitem__(key: str) -> Self
__getitem__(key: Pattern[str]) -> Self
__getitem__(
    key: SupportsIndex | slice | str | Pattern[str],
) -> Self | E
Source code in src/arcpie/project/elements.py
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
def __getitem__(self, key: SupportsIndex | slice | str | re.Pattern[str]) -> Self | E:
    if isinstance(key, (str, re.Pattern)):
        matches = type(self)()
        # Attempt direct name/uri/short name match
        if isinstance(key, str):
            matches = type(self)(
                elem for elem in self
                if key in
                    (
                        # longName ?
                        elem.name,
                        # CIM path
                        elem.uri,
                        # shortName
                        elem.name.split('\\')[-1],
                    )
            )
        # Fallback to checking re.Pattern or a "pattern like" string
        if not matches and (
            isinstance(key, re.Pattern)
            or any(op in key for op in ('*', '.', '^', '?', '$', '|'))
        ):
            pat = re.compile(key)
            matches = type(self)(
                elem for elem in self
                if pat.search(elem.name)
            )
        if not matches:
            raise IndexError(f'No elements with name {key} found')
        return matches
    if isinstance(key, slice):
        return type(self)(super().__getitem__(key))
    return super().__getitem__(key)

copy

copy() -> ElementList[E]
Source code in src/arcpie/project/elements.py
612
613
def copy(self) -> ElementList[E]:
    return type(self)(super().copy())

filter

filter(cond: Callable[[E], bool]) -> Self

Filter elements in the list using the provided function

Source code in src/arcpie/project/elements.py
586
587
588
def filter(self, cond: Callable[[E], bool]) -> Self:
    """Filter elements in the list using the provided function"""
    return type(self)(e for e in self if cond(e))

get

get(i: SupportsIndex, /, default: D = ...) -> E | D
get(s: slice, /, default: D = ...) -> Self | D
get(key: str, /, default: D = ...) -> Self | D
get(
    i: Pattern[str], /, default: D | None = None
) -> Self | D
get(
    key: SupportsIndex | str | Pattern[str] | slice,
    /,
    default: D = None,
) -> Self | E | D

Get the item from the list but return the default if it doesn't exist

Source code in src/arcpie/project/elements.py
605
606
607
608
609
610
def get[D](self, key: SupportsIndex | str | re.Pattern[str] | slice, /, default: D = None) -> Self | E | D:
    """Get the item from the list but return the default if it doesn't exist"""
    try:
        return self[key]
    except IndexError:
        return default

unwrap

unwrap(
    expects: Literal[1] = 1,
    /,
    *,
    panic: Literal[True] = True,
) -> E
unwrap(
    expects: Literal[1] = 1,
    /,
    *,
    panic: Literal[False] = False,
) -> E | KeyError
unwrap(
    expects: int = ..., /, *, panic: Literal[True] = True
) -> Self
unwrap(
    expects: int = ..., /, *, panic: Literal[False] = False
) -> Self | KeyError
unwrap(
    expects: int = 1, /, *, panic: bool = True
) -> Self | E | KeyError

Unwrap the ElementList and check to see if it has the expected number of items

PARAMETER DESCRIPTION

expects

The number of elements you expect to have (default: 1) if the number of expected elements is 1, a single element is requrned.

TYPE: int DEFAULT: 1

panic

If set to False, the Exception is returned instead of raised (default: True)

TYPE: bool DEFAULT: True

RETURNS DESCRIPTION
Element

if expects is 1

TYPE: Self | E | KeyError

Self

if expects is > 1 and expects == length

TYPE: Self | E | KeyError

UnwrapError

(KeyError subclass) if expects != length and panic is False

TYPE: Self | E | KeyError

RAISES DESCRIPTION
UnwrapError

(KeyError subclass) expects != length and panic is True

Example
>>> prj = Project(...)
>>> mp = prj.maps['My Map'].unwrap()
>>> mp
Map(My Map)
>>> mp = prj.maps['Duplicate Map'].unwrap()
Traceback (most recent call last):
    File ...
UnwrapError: 'expected 1: have 2'
>>> mp = prj.maps['Duplicate Map'].unwrap(panic=False)
>>> mp
UnwrapError('expected 1: have 2')
>>> mp = prj.maps['Duplicate Map'].unwrap(expects=2)
>>> mp
[Map(Duplicate Map), Map(Duplicate Map)]
Source code in src/arcpie/project/elements.py
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
def unwrap(self, expects: int = 1, /, *, panic: bool = True) -> Self | E | KeyError:
    """Unwrap the ElementList and check to see if it has the expected number of items

    Args:
        expects: The number of elements you expect to have (default: `1`)
            if the number of expected elements is 1, a single element is requrned.
        panic: If set to `False`, the Exception is returned instead of raised (default: `True`)

    Returns:
        Element: if `expects` is 1
        Self: if `expects` is > 1 and `expects` == length
        UnwrapError: (KeyError subclass) if `expects` != length and `panic` is `False`

    Raises:
        UnwrapError: (KeyError subclass) `expects` != length and `panic` is `True`

    Example:
        ```python
        >>> prj = Project(...)
        >>> mp = prj.maps['My Map'].unwrap()
        >>> mp
        Map(My Map)
        >>> mp = prj.maps['Duplicate Map'].unwrap()
        Traceback (most recent call last):
            File ...
        UnwrapError: 'expected 1: have 2'
        >>> mp = prj.maps['Duplicate Map'].unwrap(panic=False)
        >>> mp
        UnwrapError('expected 1: have 2')
        >>> mp = prj.maps['Duplicate Map'].unwrap(expects=2)
        >>> mp
        [Map(Duplicate Map), Map(Duplicate Map)]
        ```
    """
    if len(self) == expects:
        if expects == 1:
            return self[0]
        else:
            return self
    exc = UnwrapError(expects, len(self))
    if not panic:
        return exc
    else:
        raise exc

ElevationSource

ElevationSource(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[ElevationSource, None, Project]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

source

The string representation of the Elevation data source.

TYPE: str

unique_name

uri

visible

Visibility state of the ElevationSource.

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

source property

source: str

The string representation of the Elevation data source.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the ElevationSource.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ElevationSurface

ElevationSurface(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[ElevationSurface, CIMLayerElevationSurface, Map]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

elevation_sources

ElementList of all ElevationSources for the ElevationSurface.

TYPE: ElementList[ElevationSource]

has_map

Determine if the ElevationSurface is associated with a Map.

TYPE: bool

map

The Map that the ElevationSurface is associated with.

TYPE: Map

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

vertical_exaggeration

Vertical exaggeration of the Elevation Z values.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

elevation_sources property

elevation_sources: ElementList[ElevationSource]

ElementList of all ElevationSources for the ElevationSurface.

has_map property

has_map: bool

Determine if the ElevationSurface is associated with a Map.

map property

map: Map

The Map that the ElevationSurface is associated with.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

vertical_exaggeration property writable

vertical_exaggeration: float

Vertical exaggeration of the Elevation Z values.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

FolderConf

Bases: TypedDict

ATTRIBUTE DESCRIPTION
alias

TYPE: str

connectionString

TYPE: Path | str

isHomeFolder

TYPE: bool

alias instance-attribute

alias: str

connectionString instance-attribute

connectionString: Path | str

isHomeFolder instance-attribute

isHomeFolder: bool

GraphicElement

GraphicElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[GraphicElement, CIMGraphicElement]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
apply_style

Apply a `` StyleItem to the MapSurroundElement.

clone

Clone the GraphicElement with an optional new name. (default: {name}{n+1})

delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

apply_style

apply_style(style_item: StyleItemLike) -> None

Apply a `` StyleItem to the MapSurroundElement.

Source code in src/arcpie/project/elements.py
4977
4978
4979
4980
4981
4982
def apply_style(self, style_item: StyleItemLike) -> None:
    """Apply a `` StyleItem to the MapSurroundElement."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass not in ('POINT', 'LINE', 'POLYGON'):
        raise ValueError(f'{self} can only use `POINT`, `LINE`, or `POLYGON` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

clone

clone(name: str | None = None) -> Self

Clone the GraphicElement with an optional new name. (default: {name}{n+1})

Source code in src/arcpie/project/elements.py
4984
4985
4986
4987
4988
def clone(self, name: str | None = None) -> Self:
    """Clone the GraphicElement with an optional new name. (default: `{name}{n+1}`)"""
    new = type(self)(self.elem.clone(), self.parent)
    new.name = name or new.name
    return new

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

GroupElement

GroupElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[GroupElement, CIMGroupElement]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

elements

ElementList of all LayoutElements within the GroupElement. (use other properties for typed/filtered LayoutElements)

TYPE: ElementList[LayoutElement[Any, Any]]

graphic_elements

ElementList of all GraphicElements within the GroupElement.

TYPE: ElementList[GraphicElement]

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

group_elements

ElementList of all GroupElements within the GroupElement.

TYPE: ElementList[GroupElement]

height

LayoutElement height in page units.

TYPE: float

legend_elements

ElementList of all LegendElements within the GroupElement.

TYPE: ElementList[LegendElement]

locked

The editability state of the LayoutElement.

TYPE: bool

map_frames

ElementList of all MapFrames within the GroupElement.

TYPE: ElementList[MapFrame]

map_surround_elements

ElementList of all MapSurroundElements within the GroupElement.

TYPE: ElementList[MapSurroundElement]

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

picture_elements

ElementList of all PictureElements within the GroupElement.

TYPE: ElementList[PictureElement]

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

table_frame_elements

ElementList of all TableFrameElements within the GroupElement.

TYPE: ElementList[TableFrameElement]

text_elements

ElementList of all TextElements within the GroupElement.

TYPE: ElementList[TextElement]

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

elements property

ElementList of all LayoutElements within the GroupElement. (use other properties for typed/filtered LayoutElements)

graphic_elements property

graphic_elements: ElementList[GraphicElement]

ElementList of all GraphicElements within the GroupElement.

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

group_elements property

group_elements: ElementList[GroupElement]

ElementList of all GroupElements within the GroupElement.

height property writable

height: float

LayoutElement height in page units.

legend_elements property

legend_elements: ElementList[LegendElement]

ElementList of all LegendElements within the GroupElement.

locked property writable

locked: bool

The editability state of the LayoutElement.

map_frames property

map_frames: ElementList[MapFrame]

ElementList of all MapFrames within the GroupElement.

map_surround_elements property

map_surround_elements: ElementList[MapSurroundElement]

ElementList of all MapSurroundElements within the GroupElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

picture_elements property

picture_elements: ElementList[PictureElement]

ElementList of all PictureElements within the GroupElement.

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

table_frame_elements property

table_frame_elements: ElementList[TableFrameElement]

ElementList of all TableFrameElements within the GroupElement.

text_elements property

text_elements: ElementList[TextElement]

ElementList of all TextElements within the GroupElement.

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

GroupLayer

GroupLayer(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Layer, CIMGroupLayer, 'Map | GroupLayer']

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
add_layer
add_table
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export_lyrx
get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group_layers

TYPE: ElementList[GroupLayer]

group_type

TYPE: GroupType

layers

TYPE: ElementList[Layer]

lyrx

TYPE: dict[str, Any]

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

spatial_reference

TYPE: SpatialReference

tables

TYPE: ElementList[Table]

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group_layers property

group_layers: ElementList[GroupLayer]

group_type property

group_type: GroupType

layers property

layers: ElementList[Layer]

lyrx property

lyrx: dict[str, Any]

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

spatial_reference property

spatial_reference: SpatialReference

tables property

tables: ElementList[Table]

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

add_layer

add_layer(
    layer: LayerLike,
    position: AddPosition = 'AUTO_ARRANGE',
    *,
    copy: bool = False,
) -> Layer
Source code in src/arcpie/project/elements.py
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
def add_layer(self, layer: LayerLike, position: mpt.AddPosition = 'AUTO_ARRANGE', *, copy: bool = False) -> Layer:
    parent = self.parent
    layer = layer.elem if isinstance(layer, Element) else layer
    while not isinstance(parent, Map | None):
        parent = parent.parent
    if parent is None:
        raise ValueError(f'{self} is unbound and cannot add new layers')
    new_lay = parent.add_layer(layer, position=position, group=self)
    if copy:
        parent.refresh('layers')
        return new_lay
    else:
        parent.move_layer(layer, new_lay.elem, 'AFTER')
        parent.remove(new_lay)
        self.refresh('layers')
        return Layer(layer, self)

add_table

add_table(table: TableLike) -> Table
Source code in src/arcpie/project/elements.py
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
def add_table(self, table: TableLike) -> Table:
    parent = self.parent
    table = table.elem if isinstance(table, Element) else table
    while not isinstance(parent, Map | None):
        parent = parent.parent
    if parent is None:
        raise ValueError(f'{self} is unbound and cannot add new layers')
    new_table = parent.add_table(table, group=self)
    parent.refresh('tables')
    return new_table

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export_lyrx

export_lyrx(
    outdir: Path | str,
    *,
    name: str | None = None,
    indent: int = 2,
) -> Path
Source code in src/arcpie/project/elements.py
2302
2303
2304
2305
2306
2307
2308
2309
def export_lyrx(self, outdir: Path | str, *, name: str | None = None, indent: int = 2) -> Path:
    outdir = Path(outdir)
    name = name or self.name
    outdir = outdir / name
    outdir = outdir.with_suffix('.lyrx')
    outdir.parent.mkdir(exist_ok=True, parents=True)
    outdir.write_text(json.dumps(self.lyrx, indent=indent))
    return outdir

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

HasCentroid

Bases: Protocol

ATTRIBUTE DESCRIPTION
centroid

TYPE: Point

spatialReference

TYPE: SpatialReference

trueCentroid

TYPE: Point

centroid instance-attribute

centroid: Point

spatialReference instance-attribute

spatialReference: SpatialReference

trueCentroid instance-attribute

trueCentroid: Point

HasExtent

Bases: Protocol

ATTRIBUTE DESCRIPTION
extent

TYPE: Extent

spatialReference

TYPE: SpatialReference

extent instance-attribute

extent: Extent

spatialReference instance-attribute

spatialReference: SpatialReference

LabelClass

LabelClass(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[LabelClass, CIMLabelClass, Layer]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

expression

A string representation of the label expression in the selected language.

TYPE: str

lang_codes

TYPE: dict[str, LabelClassLanguage]

language

The language code for the LabelClass

TYPE: LabelClassLanguage

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

query

A SQL where clause to filter labeled features.

TYPE: str

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

visible

Visibility state of the LabelClass.

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

expression property writable

expression: str

A string representation of the label expression in the selected language.

lang_codes class-attribute

lang_codes: dict[str, LabelClassLanguage] = {
    'VBScript': 'VBSCRIPT',
    'JScript': 'JSCRIPT',
    'Python': 'PYTHON',
    'Arcade': 'ARCADE',
}

language property writable

language: LabelClassLanguage

The language code for the LabelClass

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

query property writable

query: str

A SQL where clause to filter labeled features.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the LabelClass.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

Layer

Layer(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Layer, CIMBaseLayer, Map | GroupLayer]

METHOD DESCRIPTION
__bool__
__eq__
__getattr__
__hash__
__iter__
__len__
__repr__
clear_selection

Clear the current selection. (same as layer.select(method='CLEAR')

clone_properties

Copy specified properties from another Layer.

create_label_class

Create a new LabelClass for the Layer.

delete

Delete the Layer.

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export_csv

Export the table data to a CSV file.

export_lyrx

Export the lyrx definition of the Layer to a file.

get_cim

Get the CIM definition for the Element.

get_prop

Get a single property or Layer.NotSupported.

get_props

Get requested layer properties.

open_view

Open the Layer table view in the active project.

query_as

Context manager for setting the active DefinitionQuery string.

refresh

Refresh all cached elements.

select

Set a selection on the Layer using a predicate or query and a method.

select_all

Select all features in the Layer. (same as layer.select(method='ALL')

selected

Get a list of all selected feature records.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

set_prop

Set a single property of the layer (if it is supported).

set_props

Set the provided properties of the Layer.

unselected

Get a list of all unselected feature records.

update_connection

Update the connection properties of the Layer.

update_from_json

Update the Layer using a WebBuilder json definition.

ATTRIBUTE DESCRIPTION
NotSupported

A sentinel for signaling that a layer property is not available for the given layer

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

chart_map

Get a mapping of all charts by type. (all chart types included, empty list for unused chart types)

TYPE: ChartMap

charts

Get Chart objects associated with the Layer.

TYPE: tuple[Chart, ...]

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_symbology

Get the symbology CIM object (or Layer.NotSupported).

TYPE: CIMRenderer

cim_symbology_type

Get the type of the CIM Symbology (or type(Layer.NotSupported))

TYPE: type[CIMRenderer]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

data_source

Get the string representation of the Layer datasource (or Layer.NotSupported).

TYPE: str

definition_queries

Get the DesinitonQueries for the Layer (empty list if no queries or not suppported).

TYPE: list[DefinitionQuery]

definition_query

Get the active DefinitionQuery string for the Layer (or Layer.NotSupported).

elem

feature_class

Get the associated FeatureClass object for the layer.

TYPE: FeatureClass

has_feature_class

Check to see if the Layer has a valid FeatureClass association.

TYPE: bool

is_geographic

Determine if the Layer is projected by comparing the SR GCS to the SR.

TYPE: bool

is_projected_on_fly

Determine if the layer is projected on the fly by comparing datasource SR to layer SR.

TYPE: bool

label_classes

Get an ElementList of all LabelClasses for the Layer.

TYPE: ElementList[LabelClass]

lyrx

A dictionary representing the lyrx definition of the Layer.

TYPE: dict[str, Any]

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

page_query

Get the active PageQuery of the layer.

TYPE: PageQuery

parent

props

Get all properties of the layer with unsupported properties being set to Layer.NotSupported

TYPE: AllLayerProps

relative_cim_dict

A copy of the CIM data with dataConnections made relative to the project.

TYPE: dict[str, Any]

relative_lyrx

A copy of the lyrx with relative pathing for dataConnections.

TYPE: dict[str, Any]

selection

Get a set of all selected feaature OIDs.

TYPE: set[int]

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

source_data

Alias for feature_class that matches Table for use in duck typed loops.

TYPE: FeatureClass

spatial_reference

Get the SR of the Layer by checking the associated FeatureClass.

TYPE: SpatialReference

supported

Get the suported properties of the Layer (see: LayerProperty)

TYPE: frozenset[str]

symbology

Get the layer symbology (or Layer.NotSupported).

TYPE: Symbology

types

Get a set of string identifiers for the Layer Type.

TYPE: set[LayerType]

unique_name

unsupported

Get the unsupported properties of the Layer (see: LayerProperty)

TYPE: frozenset[str]

uri

visible

Get the visibility state of the Layer (or Layer.NotSupported).

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

NotSupported class-attribute instance-attribute

NotSupported = NotSupported

A sentinel for signaling that a layer property is not available for the given layer Use with identity checks:

if lay.props['brightness'] is Layer.NotSupported: ... # handle

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

chart_map property

chart_map: ChartMap

Get a mapping of all charts by type. (all chart types included, empty list for unused chart types)

charts property

charts: tuple[Chart, ...]

Get Chart objects associated with the Layer.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_symbology property writable

cim_symbology: CIMRenderer

Get the symbology CIM object (or Layer.NotSupported).

cim_symbology_type property

cim_symbology_type: type[CIMRenderer]

Get the type of the CIM Symbology (or type(Layer.NotSupported))

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

data_source property writable

data_source: str

Get the string representation of the Layer datasource (or Layer.NotSupported).

definition_queries property writable

definition_queries: list[DefinitionQuery]

Get the DesinitonQueries for the Layer (empty list if no queries or not suppported).

definition_query property writable

definition_query

Get the active DefinitionQuery string for the Layer (or Layer.NotSupported).

elem instance-attribute

elem = cast(MPElem, elem)

feature_class property

feature_class: FeatureClass

Get the associated FeatureClass object for the layer.

validating connections is slow, so use has_feature_class if you need a guard

has_feature_class property

has_feature_class: bool

Check to see if the Layer has a valid FeatureClass association.

is_geographic property

is_geographic: bool

Determine if the Layer is projected by comparing the SR GCS to the SR.

is_projected_on_fly property

is_projected_on_fly: bool

Determine if the layer is projected on the fly by comparing datasource SR to layer SR.

label_classes property

label_classes: ElementList[LabelClass]

Get an ElementList of all LabelClasses for the Layer.

lyrx property

lyrx: dict[str, Any]

A dictionary representing the lyrx definition of the Layer.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

page_query property writable

page_query: PageQuery

Get the active PageQuery of the layer.

parent instance-attribute

parent = parent

props property writable

Get all properties of the layer with unsupported properties being set to Layer.NotSupported

relative_cim_dict property

relative_cim_dict: dict[str, Any]

A copy of the CIM data with dataConnections made relative to the project.

relative_lyrx property

relative_lyrx: dict[str, Any]

A copy of the lyrx with relative pathing for dataConnections.

selection property writable

selection: set[int]

Get a set of all selected feaature OIDs.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

source_data property

source_data: FeatureClass

Alias for feature_class that matches Table for use in duck typed loops.

spatial_reference property

spatial_reference: SpatialReference

Get the SR of the Layer by checking the associated FeatureClass.

supported property

supported: frozenset[str]

Get the suported properties of the Layer (see: LayerProperty)

symbology property writable

symbology: Symbology

Get the layer symbology (or Layer.NotSupported).

types property

types: set[LayerType]

Get a set of string identifiers for the Layer Type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

unsupported property

unsupported: frozenset[str]

Get the unsupported properties of the Layer (see: LayerProperty)

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Get the visibility state of the Layer (or Layer.NotSupported).

__bool__

__bool__() -> Literal[True]
Source code in src/arcpie/project/elements.py
2838
2839
2840
def __bool__(self) -> Literal[True]:
    # Override __bool__ to prevent __iter__/__len__ from making a layer falsey
    return True

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
2842
2843
def __hash__(self) -> int:
    return id(self)

__iter__

__iter__() -> Iterator[dict[str, Any]]
Source code in src/arcpie/project/elements.py
2833
2834
2835
2836
def __iter__(self) -> Iterator[dict[str, Any]]:
    if self.has_feature_class:
        return iter(self.feature_class)
    return iter(())

__len__

__len__() -> int
Source code in src/arcpie/project/elements.py
2828
2829
2830
2831
def __len__(self) -> int:
    if self.has_feature_class:
        return len(self.feature_class)
    return 0

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

clear_selection

clear_selection() -> Self

Clear the current selection. (same as layer.select(method='CLEAR')

Source code in src/arcpie/project/elements.py
2909
2910
2911
def clear_selection(self) -> Self:
    """Clear the current selection. (same as `layer.select(method='CLEAR'`)"""
    return self.select(method='CLEAR')

clone_properties

clone_properties(
    from_layer: LayerLike,
    properties: LayerPasteProperties = 'ALL',
)

Copy specified properties from another Layer.

Source code in src/arcpie/project/elements.py
3006
3007
3008
3009
def clone_properties(self, from_layer: LayerLike, properties: mpt.LayerPasteProperties = 'ALL'):
    """Copy specified properties from another Layer."""
    from_layer = from_layer.elem if isinstance(from_layer, Element) else from_layer
    self.elem.pasteProperties(from_layer, properties)

create_label_class

create_label_class(
    name: str,
    expression: str,
    query: str | None = ...,
    language: Literal['VBSCRIPT'] = 'VBSCRIPT',
) -> LabelClass
create_label_class(
    name: str,
    expression: str,
    query: str | None = ...,
    language: LabelClassLanguage = ...,
) -> LabelClass
create_label_class(
    name: str,
    expression: str,
    query: str | None = None,
    language: LabelClassLanguage = 'ARCADE',
) -> LabelClass

Create a new LabelClass for the Layer.

PARAMETER DESCRIPTION

name

The name of the new LabelClass.

TYPE: str

expression

An expression to use for the LabelClass.

TYPE: str

query

An optional SQL where clause to filter the labeled features with.

TYPE: str | None DEFAULT: None

language

The expression language. (default: ARCADE)

TYPE: LabelClassLanguage DEFAULT: 'ARCADE'

Source code in src/arcpie/project/elements.py
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
def create_label_class(self, name: str, expression: str, query: str | None = None, language: mpt.LabelClassLanguage = 'ARCADE') -> LabelClass:
    """Create a new LabelClass for the Layer.

    Args:
        name: The name of the new LabelClass.
        expression: An expression to use for the LabelClass.
        query: An optional SQL where clause to filter the labeled features with.
        language: The expression language. (default: `ARCADE`)
    """
    if 'showLabels' in self.supported:
        lc = LabelClass(self.elem.createLabelClass(name, expression, query, language), self)
        self.refresh('label_classes')
        return lc
    return type(self).NotSupported  # type: ignore

delete

delete() -> None

Delete the Layer.

Source code in src/arcpie/project/elements.py
2799
2800
2801
2802
2803
2804
2805
2806
def delete(self) -> None:
    """Delete the Layer."""
    parent = self.parent
    while not isinstance(parent, Map | None):
        parent = parent.parent
    if parent is None:
        raise AttributeError(f'{self} has no associated map and no spatial reference')
    parent.remove(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export_csv

export_csv(
    outdir: Path | str,
    *,
    name: str | None = None,
    fields: Iterable[str] | None = None,
    sep: str = ',',
    newline: str = '\n',
) -> Path

Export the table data to a CSV file.

PARAMETER DESCRIPTION

outdir

The output directory or file.

TYPE: Path | str

name

An optional name to give the file (default: layer.name)

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
def export_csv(self, outdir: Path | str,
               *,
               name: str | None = None, fields: Iterable[str] | None = None, sep: str = ',', newline: str = '\n') -> Path:
    """Export the table data to a CSV file.

    Args:
        outdir: The output directory or file.
        name: An optional name to give the file (default: `layer.name`)
    """
    outdir = Path(outdir)
    outfile = outdir.with_name(name or self.name).with_suffix('.csv')
    outfile.parent.mkdir(exist_ok=True, parents=True)
    table = self.feature_class
    fields = fields or table.fields
    with outfile.open('wt', encoding='utf-8', newline=newline) as fl:
        fl.write(sep.join(fields))
        fl.write(newline)
        fl.writelines(sep.join(line) for line in table[tuple(fields)])
    return outfile

export_lyrx

export_lyrx(
    outdir: Path | str,
    *,
    name: str | None = None,
    indent: int | None = None,
    relative: bool = True,
) -> Path

Export the lyrx definition of the Layer to a file.

PARAMETER DESCRIPTION

outdir

The directory to place the lyrx file in.

TYPE: Path | str

name

An optional name for the lyrx file. (default is layer.short_name)

TYPE: str | None DEFAULT: None

indent

Indentation level in the output lyrx file. (default: None)

TYPE: int | None DEFAULT: None

relative

Convert all absolute paths in the lyrx file to relative paths. (default: True)

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
def export_lyrx(self, outdir: Path | str, *, name: str | None = None, indent: int | None = None, relative: bool = True) -> Path:
    """Export the `lyrx` definition of the Layer to a file.

    Args:
        outdir: The directory to place the `lyrx` file in.
        name: An optional name for the `lyrx` file. (default is `layer.short_name`)
        indent: Indentation level in the output `lyrx` file. (default: `None`)
        relative: Convert all absolute paths in the `lyrx` file to relative paths. (default: `True`)
    """
    outdir = Path(outdir)
    name = name or self.short_name
    outdir = outdir / name
    outdir = outdir.with_suffix('.lyrx')
    outdir.parent.mkdir(exist_ok=True, parents=True)
    outdir.write_text(json.dumps(self.relative_lyrx if relative else self.lyrx, indent=indent))
    return outdir

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

get_prop

get_prop(prop: LayerProperty) -> Any

Get a single property or Layer.NotSupported.

Source code in src/arcpie/project/elements.py
2601
2602
2603
2604
2605
def get_prop(self, prop: LayerProperty) -> Any:
    """Get a single property or `Layer.NotSupported`."""
    if prop not in LayerProps:
        return type(self).NotSupported
    return getattr(self.elem, prop, type(self).NotSupported)

get_props

get_props(*props: LayerProperty) -> SomeLayerProps

Get requested layer properties.

Note

*props is case insensitive (underscores are replaced with empty strings) (e.g. dataSource == DATASOURCE == datasource == data_source). The returned dictionary will use the supplied case. (e.g. layer.get_props('DATASOURCE') -> {'DATASOURCE': ...}) if a property is not supported, Layer.NotSupported is set as the value. (e.g layer.get_props('brightness') -> {'brightness': Layer.NotSupported})

Raises: ValueError: If a property is not a layer property

Source code in src/arcpie/project/elements.py
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
def get_props(self, *props: LayerProperty) -> SomeLayerProps:
    """Get requested layer properties.

    Note:
        `*props` is case insensitive (underscores are replaced with empty strings)
        (e.g. `dataSource` == `DATASOURCE` == `datasource` == `data_source`).
        The returned dictionary will use the supplied case.
        (e.g. `layer.get_props('DATASOURCE')` -> `{'DATASOURCE': ...}`)
        if a property is not supported, `Layer.NotSupported` is set as the value.
        (e.g `layer.get_props('brightness') -> {'brightness': Layer.NotSupported}`)
    Raises:
        ValueError: If a property is not a layer property
    """
    p_map = _PropMap
    invalid = [
        p_map[p] for p in
        {p.lower().replace('_', '') for p in props}.difference(p_map.keys())
    ]
    if invalid:
        raise ValueError(
            f'{invalid} are not valid layer properties: {list(LayerProps)}'
        )
    props = props or cast(tuple[LayerProperty], tuple(self.supported))
    caseless_props = [p.lower().replace('_', '') for p in props]
    return cast(SomeLayerProps, {
        prop: (
            getattr(self.elem, prop_name)
            if prop_name in self.supported
            else type(self).NotSupported
        )
        for prop, c_prop in zip(props, caseless_props, strict=True)
        if (prop_name := p_map.get(c_prop))
    })

open_view

open_view(selected: bool = False) -> None

Open the Layer table view in the active project.

Source code in src/arcpie/project/elements.py
3017
3018
3019
def open_view(self, selected: bool = False) -> None:
    """Open the Layer table view in the active project."""
    self.elem.openTableView(selected)

query_as

query_as(query: str | None)

Context manager for setting the active DefinitionQuery string.

Example:

>>> print(len(lay))
250
>>> with lay.query_as("STATUS = 'ACTIVE'"):
...     print(len(lay))
125

Source code in src/arcpie/project/elements.py
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
@contextmanager
def query_as(self, query: str | None):
    """Context manager for setting the active DefinitionQuery string.

    Example:
    ```python
    >>> print(len(lay))
    250
    >>> with lay.query_as("STATUS = 'ACTIVE'"):
    ...     print(len(lay))
    125
    ```
    """
    cur = self.definition_query
    try:
        self.definition_query = query
        yield self
    finally:
        self.definition_query = cur

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

select

select(
    *,
    predicate: Callable[[dict[str, Any]], bool]
    | None = None,
    query: str | None = None,
    method: Method | Literal['CLEAR', 'ALL'] = 'NEW',
) -> Self

Set a selection on the Layer using a predicate or query and a method.

PARAMETER DESCRIPTION

predicate

A function that takes a feature record and returns True if it is to be selected.

TYPE: Callable[[dict[str, Any]], bool] | None DEFAULT: None

query

A SQL where clause that will define the selection (ignored if predicate is set).

TYPE: str | None DEFAULT: None

method

How to resolve the selection with existing selection.

TYPE: Method | Literal['CLEAR', 'ALL'] DEFAULT: 'NEW'

Note

Calling with method = 'CLEAR' and no other arguments will clear the selection. Calling with method = 'ALL' and no other arguments will select all features.

Source code in src/arcpie/project/elements.py
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
def select(self,
           *,
           predicate: Callable[[dict[str, Any]], bool] | None = None,
           query: str | None = None,
           method: mpt.Method | Literal['CLEAR', 'ALL'] = 'NEW',
) -> Self:
    """Set a selection on the Layer using a predicate or query and a method.

    Args:
        predicate: A function that takes a feature record and returns True if it is to be selected.
        query: A SQL where clause that will define the selection (ignored if `predicate` is set).
        method: How to resolve the selection with existing selection.

    Note:
        Calling with `method = 'CLEAR'` and no other arguments will clear the selection.
        Calling with `method = 'ALL'` and no other arguments will select all features.
    """
    if method == 'CLEAR':
        self.elem.setSelectionSet([])
        return self

    if method == 'ALL':
        self.elem.setSelectionSet(list(self.feature_class['OID@']))
        return self

    if predicate is not None:
        self.elem.setSelectionSet(
            [r['OID@'] for r in self.feature_class if predicate(r)],
            method
        )
        return self

    with self.query_as(query or self.definition_query):
        self.elem.setSelectionSet(list(self.feature_class['OID@']), method)

    return self

select_all

select_all() -> Self

Select all features in the Layer. (same as layer.select(method='ALL')

Source code in src/arcpie/project/elements.py
2913
2914
2915
def select_all(self) -> Self:
    """Select all features in the Layer. (same as `layer.select(method='ALL'`)"""
    return self.select(method='ALL')

selected

selected() -> list[dict[str, Any]]

Get a list of all selected feature records.

Source code in src/arcpie/project/elements.py
2899
2900
2901
2902
def selected(self) -> list[dict[str, Any]]:
    """Get a list of all selected feature records."""
    oids = self.elem.getSelectionSet() or set()
    return [row for row in self.feature_class if row['OID@'] in oids]

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

set_prop

set_prop(prop: LayerProperty, val: Any) -> None

Set a single property of the layer (if it is supported).

Source code in src/arcpie/project/elements.py
2615
2616
2617
2618
2619
def set_prop(self, prop: LayerProperty, val: Any) -> None:
    """Set a single property of the layer (if it is supported)."""
    if self.get_prop(prop) is type(self).NotSupported:
        return
    setattr(self.elem, prop, val)

set_props

set_props(**props: Unpack[SomeLayerProps]) -> None

Set the provided properties of the Layer.

Source code in src/arcpie/project/elements.py
2607
2608
2609
2610
2611
2612
2613
def set_props(self, **props: Unpack[SomeLayerProps]) -> None:
    """Set the provided properties of the Layer."""
    p_map = _PropMap
    for prop, val in props.items():
        prop = p_map.get(prop.lower())
        if prop in self.supported:
            setattr(self.elem, prop, val)

unselected

unselected() -> list[dict[str, Any]]

Get a list of all unselected feature records.

Source code in src/arcpie/project/elements.py
2904
2905
2906
2907
def unselected(self) -> list[dict[str, Any]]:
    """Get a list of all unselected feature records."""
    oids = self.elem.getSelectionSet() or set()
    return [row for row in self.feature_class if row['OID@'] not in oids]

update_connection

update_connection(
    new: str,
    current: str | None = None,
    auto_update: bool = True,
    validate: bool = True,
    ignore_case: bool = False,
) -> None

Update the connection properties of the Layer.

PARAMETER DESCRIPTION

new

The new connection string.

TYPE: str

current

The connection string to replace.

TYPE: str | None DEFAULT: None

auto_update

Update joins and relates.

TYPE: bool DEFAULT: True

validate

Validate the Layer connection before setting it.

TYPE: bool DEFAULT: True

ignore_case

Ignore case in queries.

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
def update_connection(self,
                      new: str, current: str | None = None,
                      auto_update: bool = True,
                      validate: bool = True,
                      ignore_case: bool = False,
    ) -> None:
    """Update the connection properties of the Layer.

    Args:
        new: The new connection string.
        current: The connection string to replace.
        auto_update: Update joins and relates.
        validate: Validate the Layer connection before setting it.
        ignore_case: Ignore case in queries.
    """
    self.elem.updateConnectionProperties(current, new, auto_update, validate, ignore_case)

update_from_json

update_from_json(data: str | dict[str, Any]) -> None

Update the Layer using a WebBuilder json definition.

Source code in src/arcpie/project/elements.py
3011
3012
3013
3014
3015
def update_from_json(self, data: str | dict[str, Any]) -> None:
    """Update the Layer using a WebBuilder json definition."""
    if isinstance(data, dict):
        data = json.dumps(data)
    self.elem.updateLayerFromJSON(data)

LayerConnectionConf

Bases: TypedDict

ATTRIBUTE DESCRIPTION
connection_info

TYPE: dict[str, str | bool]

dataset

TYPE: str

workspace_factory

TYPE: str

connection_info instance-attribute

connection_info: dict[str, str | bool]

dataset instance-attribute

dataset: str

workspace_factory instance-attribute

workspace_factory: str

Layout

Layout(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Layout, CIMLayout, Project]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
create_bookmark_mapseries

Create a BookmarkMapseries for the Layout.

create_map_frame

Create a new MapFrame in the Layout.

create_map_surround_element

Create a new MapSurroundElement in the Layout.

create_mapseries

Create a spatial Mapseries for the Layout.

create_table_frame_element

Create a new TableFrameElement in the Layout.

delete_element

Delete an element from the Layout.

delete_elements

Delete multiple elements from the Layout (varargs).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export

Export the Layout.

get_cim

Get the CIM definition for the Element.

open_view

Open the Layout in the active Project.

refresh

Refresh all cached elements.

resize

Resize the Layout.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
bookmark_mapseries

The BookmarkMapseries for the Layout if it exists. (raises: AttributeError)

TYPE: BookmarkMapSeries

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

color_model

The Layout width in ColorModel.

TYPE: ColorModel

elem

elements

ElementList of all LayoutElements in the Layout. (use other accessors for typed/filtered Elements.)

TYPE: ElementList[LayoutElement[Any, Any]]

graphic_elements

ElementList of all GraphicElements in the Layout.

TYPE: ElementList[GraphicElement]

group_elements

ElementList of all GroupElements in the Layout.

TYPE: ElementList[GroupElement]

has_bookmark_mapseries

Determine if the Layout has a BookmarkMapseries.

TYPE: bool

has_mapseries

Determine if the Layout has a spatial Mapseries.

TYPE: bool

height

The Layout height in page units.

TYPE: float

map_frames

ElementList of all MapFrames in the Layout.

TYPE: ElementList[MapFrame]

map_surround_elements

ElementList of all MapSurroundElements in the Layout.

TYPE: ElementList[MapSurroundElement]

mapseries

The spatial Mapseries for the Layout if it exists. (raises: AttributeError)

TYPE: MapSeries

metadata

The Layout Metadata.

TYPE: Metadata

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

picture_elements

ElementList of all PictureElements in the Layout.

TYPE: ElementList[PictureElement]

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

table_frame_elements

ElementList of all TableFrameElements in the Layout.

TYPE: ElementList[TableFrameElement]

text_elements

ElementList of all TextElements in the Layout.

TYPE: ElementList[TextElement]

unique_name

units

The Layout page units.

TYPE: PageUnits

uri

width

The Layout width in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

bookmark_mapseries property

bookmark_mapseries: BookmarkMapSeries

The BookmarkMapseries for the Layout if it exists. (raises: AttributeError)

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

color_model property writable

color_model: ColorModel

The Layout width in ColorModel.

elem instance-attribute

elem = cast(MPElem, elem)

elements property

ElementList of all LayoutElements in the Layout. (use other accessors for typed/filtered Elements.)

graphic_elements property

graphic_elements: ElementList[GraphicElement]

ElementList of all GraphicElements in the Layout.

group_elements property

group_elements: ElementList[GroupElement]

ElementList of all GroupElements in the Layout.

has_bookmark_mapseries property

has_bookmark_mapseries: bool

Determine if the Layout has a BookmarkMapseries.

has_mapseries property

has_mapseries: bool

Determine if the Layout has a spatial Mapseries.

height property writable

height: float

The Layout height in page units.

map_frames property

map_frames: ElementList[MapFrame]

ElementList of all MapFrames in the Layout.

map_surround_elements property

map_surround_elements: ElementList[MapSurroundElement]

ElementList of all MapSurroundElements in the Layout.

mapseries property

mapseries: MapSeries

The spatial Mapseries for the Layout if it exists. (raises: AttributeError)

metadata property writable

metadata: Metadata

The Layout Metadata.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

picture_elements property

picture_elements: ElementList[PictureElement]

ElementList of all PictureElements in the Layout.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

table_frame_elements property

table_frame_elements: ElementList[TableFrameElement]

ElementList of all TableFrameElements in the Layout.

text_elements property

text_elements: ElementList[TextElement]

ElementList of all TextElements in the Layout.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

units property writable

units: PageUnits

The Layout page units.

uri instance-attribute

uri = _get_uri(self.elem)

width property writable

width: float

The Layout width in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

create_bookmark_mapseries

create_bookmark_mapseries(
    frame: MapFrameLike,
    bookmarks: Iterable[Bookmark]
    | Iterable[Bookmark]
    | None = None,
) -> BookmarkMapSeries

Create a BookmarkMapseries for the Layout.

PARAMETER DESCRIPTION

frame

The MapFrame to use for the BookmarkMapseries (must have Bookmarks).

TYPE: MapFrameLike

bookmarks

The Bookmarks in the Map to use. (default: All)

TYPE: Iterable[Bookmark] | Iterable[Bookmark] | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
def create_bookmark_mapseries(
    self,
    frame: MapFrameLike,
    bookmarks: Iterable[Bookmark] | Iterable[mpt.Bookmark] | None = None,
) -> BookmarkMapSeries:
    """Create a BookmarkMapseries for the Layout.

    Args:
        frame: The MapFrame to use for the BookmarkMapseries (must have Bookmarks).
        bookmarks: The Bookmarks in the Map to use. (default: All)
    """
    if isinstance(frame, MapFrame):
        frame = frame.elem
    bookmarks = [
        b.elem if isinstance(b, Bookmark) else b
        for b in bookmarks or []
    ]
    return BookmarkMapSeries(self.elem.createBookmarkMapSeries(frame, bookmarks), self)

create_map_frame

create_map_frame(
    *,
    name: str | None = None,
    map: MapLike | None = None,
    geometry: Polygon | Point | HasCentroid | None = None,
) -> MapFrame

Create a new MapFrame in the Layout.

PARAMETER DESCRIPTION

geometry

An anchor point or polygon shape for the MapFrame (using page units). (default: Point(0,0))

TYPE: Polygon | Point | HasCentroid | None DEFAULT: None

map

The Map to associate the MapFrame with.

TYPE: MapLike | None DEFAULT: None

name

The name of the MapFrame element.

TYPE: str | None DEFAULT: None

Notes

Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid to set the anchor point of the MapFrame. After creating the MapFrame, the anchor point, width, and height can be set directly. If no geometry is used, the MapFrame will be created at the Layout origin.

Source code in src/arcpie/project/elements.py
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
def create_map_frame(
    self,
    *,
    name: str | None = None,
    map: MapLike | None = None,
    geometry: Polygon | Point | HasCentroid | None = None,
) -> MapFrame:
    """Create a new MapFrame in the Layout.

    Args:
        geometry: An anchor point or polygon shape for the MapFrame (using page units). (default: `Point(0,0)`)
        map: The Map to associate the MapFrame with.
        name: The name of the MapFrame element.

    Notes:
        Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid
        to set the anchor point of the MapFrame.
        After creating the MapFrame, the anchor point, width, and height can be set directly.
        If no geometry is used, the MapFrame will be created at the Layout origin.
    """
    if geometry is None:
        geometry = Point(0, 0)
    if not isinstance(geometry, Polygon | Point):
        if not isinstance(cast(Any, geometry), HasCentroid):
            raise ValueError(
                f'Invalid source geometry {type(geometry).__name__} for MapFrame, '
                'must have trueCentroid, centroid and spatialReference attributes'
            )
        geometry = geometry.centroid
    if isinstance(map, Map):
        map = map.elem
    return MapFrame(self.elem.createMapFrame(geometry, map, name), self)

create_map_surround_element

create_map_surround_element(
    surround_type: MapSurroundType,
    *,
    name: str | None = None,
    frame: MapFrameLike | None = None,
    geometry: Polygon | Point | HasCentroid | None = None,
    style_item: StyleItemLike | None = None,
) -> MapSurroundElement

Create a new MapSurroundElement in the Layout.

PARAMETER DESCRIPTION

name

An optional name for the MapSurroundElement.

TYPE: str | None DEFAULT: None

surround_type

The MapSurroundElement type.

TYPE: MapSurroundType

geometry

An anchor point or polygon shape for the MapSurroundElement (using page units). (default: Point(0,0))

TYPE: Polygon | Point | HasCentroid | None DEFAULT: None

frame

The MapFrame to associate the MapSurroundElement with.

TYPE: MapFrameLike | None DEFAULT: None

style_item

A style item to use for the MapSurroundElement style.

TYPE: StyleItemLike | None DEFAULT: None

Notes

Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid to set the anchor point of the MapSurroundElement. After creating the MapSurroundElement, the anchor point, width, and height can be set directly. If no geometry is used, the MapSurroundElement will be created at the Layout origin.

Source code in src/arcpie/project/elements.py
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
def create_map_surround_element(
    self,
    surround_type: mpt.MapSurroundType,
    *,
    name: str | None = None,
    frame: MapFrameLike | None = None,
    geometry: Polygon | Point | HasCentroid | None = None,
    style_item: StyleItemLike | None = None,
) -> MapSurroundElement:
    """Create a new MapSurroundElement in the Layout.

    Args:
        name: An optional name for the MapSurroundElement.
        surround_type: The MapSurroundElement type.
        geometry: An anchor point or polygon shape for the MapSurroundElement (using page units). (default: `Point(0,0)`)
        frame: The MapFrame to associate the MapSurroundElement with.
        style_item: A style item to use for the MapSurroundElement style.

    Notes:
        Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid
        to set the anchor point of the MapSurroundElement.
        After creating the MapSurroundElement, the anchor point, width, and height can be set directly.
        If no geometry is used, the MapSurroundElement will be created at the Layout origin.
    """
    if geometry is None:
        geometry = Point(0, 0)
    if not isinstance(geometry, Polygon | Point):
        if not isinstance(cast(Any, geometry), HasCentroid):
            raise ValueError(
                f'Invalid source geometry {type(geometry).__name__} for MapFrame, '
                'must have trueCentroid, centroid and spatialReference attributes'
            )
        geometry = geometry.centroid
    if isinstance(frame, MapFrame):
        frame = frame.elem
    if isinstance(style_item, StyleItem):
        style_item = style_item.elem
    return MapSurroundElement(self.elem.createMapSurroundElement(geometry, surround_type, frame, style_item, name), self)

create_mapseries

create_mapseries(
    frame: MapFrameLike,
    layer: LayerLike,
    name_field: str,
    sort_field: str | None = None,
) -> MapSeries

Create a spatial Mapseries for the Layout.

PARAMETER DESCRIPTION

frame

The MapFrame to use for the Mapseries.

TYPE: MapFrameLike

layer

The series layer in the MapFrame Map.

TYPE: LayerLike

name_field

The field to use as the Mapseries page name.

TYPE: str

sort_field

The field to sorth the Maperies pages by (default: name_field).

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
def create_mapseries(
    self,
    frame: MapFrameLike,
    layer: LayerLike,
    name_field: str,
    sort_field: str | None = None,
) -> MapSeries:
    """Create a spatial Mapseries for the Layout.

    Args:
        frame: The MapFrame to use for the Mapseries.
        layer: The series layer in the MapFrame Map.
        name_field: The field to use as the Mapseries page name.
        sort_field: The field to sorth the Maperies pages by (default: `name_field`).
    """
    if isinstance(frame, MapFrame):
        frame = frame.elem
    if isinstance(layer, Layer):
        layer = layer.elem
    return MapSeries(self.elem.createSpatialMapSeries(frame, layer, name_field, sort_field), self)

create_table_frame_element

create_table_frame_element(
    *,
    name: str | None = None,
    frame: MapFrameLike | None = None,
    table: LayerLike | TableLike | None = None,
    fields: Iterable[str] | None = None,
    geometry: Point | Polygon | HasCentroid | None = None,
    style_item: StyleItemLike | None = None,
) -> TableFrameElement

Create a new TableFrameElement in the Layout.

PARAMETER DESCRIPTION

name

An optional name for the TableFrameElement.

TYPE: str | None DEFAULT: None

geometry

An anchor point or polygon shape for the TableFrameElement (using page units). (default: Point(0,0))

TYPE: Point | Polygon | HasCentroid | None DEFAULT: None

frame

The MapFrame to associate the TableFrameElement with.

TYPE: MapFrameLike | None DEFAULT: None

table

The Table or Layer in the MapFrame to use as the TableFrameElement source.

TYPE: LayerLike | TableLike | None DEFAULT: None

fields

A list of fields to include in the TableFrameElement.

TYPE: Iterable[str] | None DEFAULT: None

style_item

A style item to use for the TableFrameElement style.

TYPE: StyleItemLike | None DEFAULT: None

Notes

Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid to set the anchor point of the TableFrameElement. After creating the TableFrameElement, the anchor point, width, and height can be set directly. If no geometry is used, the TableFrameElement will be created at the Layout origin.

Source code in src/arcpie/project/elements.py
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
def create_table_frame_element(
    self,
    *,
    name: str | None = None,
    frame: MapFrameLike | None = None,
    table: LayerLike | TableLike | None = None,
    fields: Iterable[str] | None = None,
    geometry: Point | Polygon | HasCentroid | None = None,
    style_item: StyleItemLike | None = None,
) -> TableFrameElement:
    """Create a new TableFrameElement in the Layout.

    Args:
        name: An optional name for the TableFrameElement.
        geometry: An anchor point or polygon shape for the TableFrameElement (using page units). (default: `Point(0,0)`)
        frame: The MapFrame to associate the TableFrameElement with.
        table: The Table or Layer in the MapFrame to use as the TableFrameElement source.
        fields: A list of fields to include in the TableFrameElement.
        style_item: A style item to use for the TableFrameElement style.

    Notes:
        Use a Polygon created in Layout coordinates to specify an exact shape, or use a shape with a centroid
        to set the anchor point of the TableFrameElement.
        After creating the TableFrameElement, the anchor point, width, and height can be set directly.
        If no geometry is used, the TableFrameElement will be created at the Layout origin.
    """
    if geometry is None:
        geometry = Point(0, 0)
    if not isinstance(geometry, Polygon | Point):
        if not isinstance(cast(Any, geometry), HasCentroid):
            raise ValueError(
                f'Invalid source geometry {type(geometry).__name__} for MapFrame, '
                'must have trueCentroid, centroid and spatialReference attributes'
            )
        geometry = geometry.centroid
    if isinstance(frame, MapFrame):
        frame = frame.elem
    if isinstance(style_item, StyleItem):
        style_item = style_item.elem
    if isinstance(table, Layer | Table):
        table = table.elem
    fields = list(fields) if fields else None
    return TableFrameElement(self.elem.createTableFrameElement(geometry, frame, table, fields, style_item, name), self)

delete_element

delete_element(
    elem: LayoutElement[LayoutElement, Any] | LayoutElement,
)

Delete an element from the Layout.

Source code in src/arcpie/project/elements.py
3662
3663
3664
3665
3666
def delete_element(self, elem: LayoutElement[mpt.LayoutElement, Any] | mpt.LayoutElement):
    """Delete an element from the Layout."""
    if isinstance(elem, LayoutElement):
        elem = elem.elem
    self.elem.deleteElement(elem)

delete_elements

delete_elements(
    *elems: LayoutElement[LayoutElement, Any]
    | LayoutElement,
) -> None

Delete multiple elements from the Layout (varargs).

Source code in src/arcpie/project/elements.py
3668
3669
3670
3671
def delete_elements(self, *elems: LayoutElement[mpt.LayoutElement, Any] | mpt.LayoutElement) -> None:
    """Delete multiple elements from the Layout (varargs)."""
    for elem in elems:
        self.delete_element(elem)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export

export(
    format: Format | ExportFormat | Format,
    *,
    outfile: None = None,
    antialiasing: Antialiasing | None = ...,
) -> bytes
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str = ...,
    antialiasing: Antialiasing | None = ...,
) -> Path
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str | None = None,
    antialiasing: Antialiasing | None = None,
) -> Path | bytes

Export the Layout.

PARAMETER DESCRIPTION

format

A Format object or a string format (string option will use defaults)

TYPE: Format | ExportFormat | Format

outfile

An optional output file location to use (will override format filePath)

TYPE: Path | str | None DEFAULT: None

antialiasing

Antialiasing options for the output file

TYPE: Antialiasing | None DEFAULT: None

RETURNS DESCRIPTION
Path

If outfile is set

TYPE: Path | bytes

bytes

If outfile is unset

TYPE: Path | bytes

Source code in src/arcpie/project/elements.py
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
def export(self,
           format: mp.Format | mpt.ExportFormat | fmts.Format,
           *,
           outfile: Path | str | None = None,
           antialiasing: mpt.Antialiasing | None = None,
    ) -> Path | bytes:
    """Export the Layout.

    Args:
        format: A Format object or a string format (string option will use defaults)
        outfile: An optional output file location to use (will override format filePath)
        antialiasing: Antialiasing options for the output file

    Returns:
        Path: If outfile is set
        bytes: If outfile is unset
    """
    display = None
    outfile = Path(outfile) if outfile else None
    if antialiasing:
        display = cast(mpt.DisplayOptions, mp.CreateExportOptions('DISPLAY'))
        display.setAntialiasing(antialiasing)

    if isinstance(format, fmts.Format):
        format = format.fmt

    if type(format).__name__.endswith('Format'):
        suffix = type(format).__name__.replace('Format', '').lower()
        format = cast(mpt.ExportFormat, format)

    elif isinstance(format, str):
        suffix = format.lower()
        format = mp.CreateExportFormat(format)

    else:
        raise ValueError(f'Unknown format {type(format)} : {format}')

    if suffix == 'jpeg':
        suffix = 'jpg'

    with tempfile.TemporaryDirectory() as tmp:
        tmp = Path(tmp)
        out = tmp / f'{self.name}.{suffix}'
        format.filePath = str(out)
        self.elem.export(format, display_options=display)
        data = out.read_bytes()
    if outfile:
        outfile.parent.mkdir(exist_ok=True, parents=True)
        outfile.with_suffix(suffix).write_bytes(data)
        return outfile
    else:
        return data

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

open_view

open_view()

Open the Layout in the active Project.

Source code in src/arcpie/project/elements.py
3752
3753
3754
def open_view(self):
    """Open the Layout in the active Project."""
    self.elem.openView()

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

resize

resize(
    width: float | None = None,
    height: float | None = None,
    resize_elements: bool = True,
) -> None

Resize the Layout.

PARAMETER DESCRIPTION

width

The new Layout width.

TYPE: float | None DEFAULT: None

height

The new Layout height.

TYPE: float | None DEFAULT: None

resize_elements

Resize all child elements to maintain scale. (default: True)

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
def resize(self, width: float | None = None, height: float | None = None, resize_elements: bool = True) -> None:
    """Resize the Layout.

    Args:
        width: The new Layout width.
        height: The new Layout height.
        resize_elements: Resize all child elements to maintain scale. (default: `True`)
    """
    width = width or self.width
    height = height or self.height
    self.elem.changePageSize(width, height, resize_elements)

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

LayoutElement

LayoutElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[MPElem, CIM, Layout]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

LegendElement

LegendElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[LegendElement, CIMLegend]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
add_item

Add a Layer to the Legend.

apply_style

Apply a LEGEND style item to the LegendElement.

delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

move_item

Move a LegendItem to a different location in the LegendElement.

refresh

Refresh all cached elements.

remove_item

Remove a LegedItem from the LegendElement.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

sync

Set sync properties for the LegendElement.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

columns

The number of columns in the Legend.

TYPE: int

elem

fitting_strategy

The fitting strategy of the LegendElement.

TYPE: LegendFittingStrategy

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

has_map_frame

Determine if the LegendElement has an associated MapFrame.

TYPE: bool

height

LayoutElement height in page units.

TYPE: float

items

ElementList of LegendItems in the LegendElement.

TYPE: ElementList[LegendItem]

locked

The editability state of the LayoutElement.

TYPE: bool

map

TYPE: Map

map_frame

Get the MapFrame associated with the LegendElement. (raises: AttributeError)

TYPE: MapFrame

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

overflowing

Determine if the LegendElement is overflowing its bounds.

TYPE: bool

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

show_title

Title visibility state of the LegendElement.

TYPE: bool

sync_new

Add new Layers to the LegendElement when they are added to the Map.

TYPE: bool

sync_order

TYPE: bool

sync_scale

Sync the reference scale of the LegendElement with the Map reference scale.

TYPE: bool

sync_visibility

Sync legend visibility with Map Layer visibility.

TYPE: bool

title

The title of the LegendElement.

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

columns property writable

columns: int

The number of columns in the Legend.

elem instance-attribute

elem = cast(MPElem, elem)

fitting_strategy property writable

fitting_strategy: LegendFittingStrategy

The fitting strategy of the LegendElement.

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

has_map_frame property

has_map_frame: bool

Determine if the LegendElement has an associated MapFrame.

height property writable

height: float

LayoutElement height in page units.

items property

ElementList of LegendItems in the LegendElement.

Items can be mutated directly to update them. No need or ability to set this.

locked property writable

locked: bool

The editability state of the LayoutElement.

map property

map: Map

map_frame property writable

map_frame: MapFrame

Get the MapFrame associated with the LegendElement. (raises: AttributeError)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

overflowing property

overflowing: bool

Determine if the LegendElement is overflowing its bounds.

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

show_title property writable

show_title: bool

Title visibility state of the LegendElement.

sync_new property writable

sync_new: bool

Add new Layers to the LegendElement when they are added to the Map.

sync_order property writable

sync_order: bool

sync_scale property writable

sync_scale: bool

Sync the reference scale of the LegendElement with the Map reference scale.

sync_visibility property writable

sync_visibility: bool

Sync legend visibility with Map Layer visibility.

title property writable

title: str

The title of the LegendElement.

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

add_item

add_item(
    layer: LayerLike, position: AddPosition = 'TOP'
) -> LegendItem

Add a Layer to the Legend.

PARAMETER DESCRIPTION

layer

A Layer to add to the Legend.

TYPE: LayerLike

position

The position to add the layer in. (default: TOP)

TYPE: AddPosition DEFAULT: 'TOP'

Source code in src/arcpie/project/elements.py
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
def add_item(self, layer: LayerLike, position: mpt.AddPosition = 'TOP') -> LegendItem:
    """Add a Layer to the Legend.

    Args:
        layer: A Layer to add to the Legend.
        position: The position to add the layer in. (default: `TOP`)
    """
    layer = layer.elem if isinstance(layer, Element) else layer
    item = LegendItem(self.elem.addItem(layer, position), self)
    self.refresh('items')
    return item

apply_style

apply_style(style_item: StyleItemLike)

Apply a LEGEND style item to the LegendElement.

Source code in src/arcpie/project/elements.py
5241
5242
5243
5244
5245
5246
def apply_style(self, style_item: StyleItemLike):
    """Apply a `LEGEND` style item to the LegendElement."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass != 'LEGEND':
        raise ValueError(f'{self} can only use `LEGEND` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

move_item

move_item(
    reference: LegendItemLike,
    item: LegendItemLike,
    position: MovePosition = 'BEFORE',
)

Move a LegendItem to a different location in the LegendElement.

PARAMETER DESCRIPTION

reference

The reference LegendItem to move the item relative to.

TYPE: LegendItemLike

item

The LegendItem to move.

TYPE: LegendItemLike

position

Where to move the item relative to the reference. (default: BEFORE)

TYPE: MovePosition DEFAULT: 'BEFORE'

Source code in src/arcpie/project/elements.py
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
def move_item(self, reference: LegendItemLike, item: LegendItemLike, position: mpt.MovePosition = 'BEFORE'):
    """Move a LegendItem to a different location in the LegendElement.

    Args:
        reference: The reference LegendItem to move the item relative to.
        item: The LegendItem to move.
        position: Where to move the item relative to the reference. (default: `BEFORE`)
    """
    reference = reference.elem if isinstance(reference, Element) else reference
    item = item.elem if isinstance(item, Element) else item
    self.elem.moveItem(reference, item, position)

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

remove_item

remove_item(item: LegendItemLike) -> None

Remove a LegedItem from the LegendElement.

Source code in src/arcpie/project/elements.py
5223
5224
5225
5226
5227
def remove_item(self, item: LegendItemLike) -> None:
    """Remove a LegedItem from the LegendElement."""
    item = item.elem if isinstance(item, Element) else item
    self.elem.removeItem(item)
    self.refresh('items')

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

sync

sync(
    *on: Literal['visibility', 'new', 'scale', 'order'],
) -> None

Set sync properties for the LegendElement.

Note

Any properties not explicitly passed to this method will be disabled.

Source code in src/arcpie/project/elements.py
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
def sync(self, *on: Literal['visibility', 'new', 'scale', 'order']) -> None:
    """Set sync properties for the LegendElement.

    Note:
        Any properties not explicitly passed to this method will be disabled.
    """
    self.sync_visibility = 'visibility' in on
    self.sync_new = 'new' in on
    self.sync_scale = 'scale' in on
    self.sync_order = 'order' in on

LegendItem

LegendItem(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[LegendItem, CIMLegendItem, LegendElement]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
apply_style

Apply a LEGEND_ITEM style to the LegendItem.

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

show_visibleure_count
ATTRIBUTE DESCRIPTION
arrangement

The patch/description/label arrangement order for the LegendItem.

TYPE: LegendItemArrangement

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

column

TYPE: int

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

patch_height

The patch height of the LayoutItem in points.

TYPE: float

patch_width

The patch width of the LayoutItem in points.

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

show_feature_count

TYPE: bool

show_visible

TYPE: bool

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

arrangement property writable

arrangement: LegendItemArrangement

The patch/description/label arrangement order for the LegendItem.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

column property writable

column: int

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

patch_height property writable

patch_height: float

The patch height of the LayoutItem in points.

patch_width property writable

patch_width: float

The patch width of the LayoutItem in points.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

show_feature_count property writable

show_feature_count: bool

show_visible property

show_visible: bool

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

apply_style

apply_style(style_item: StyleItemLike) -> None

Apply a LEGEND_ITEM style to the LegendItem.

Source code in src/arcpie/project/elements.py
5312
5313
5314
5315
5316
5317
def apply_style(self, style_item: StyleItemLike) -> None:
    """Apply a `LEGEND_ITEM` style to the LegendItem."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass != 'LEGEND_ITEM':
        raise ValueError(f'{self} can only use `LEGEND_ITEM` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

show_visibleure_count

show_visibleure_count(show: bool) -> None
Source code in src/arcpie/project/elements.py
5308
5309
5310
@show_visible.setter
def show_visibleure_count(self, show: bool) -> None:
    self.elem.showVisibleFeatures = show

Map

Map(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Map, CIMMap, Project]

mp.Map wrapper that allows for simpler interacton with Maps.

Example:

>>> my_map = prj.maps['My Map$'].unwrap()
>>> my_map.layers
[Layer(Lay 1), Layer(Lay 2), ....]
>>> lay2 = my_map.layers['2$'].unwrap()
>>> lay1 = my_map.layers['1$'].unwrap()
>>> my_map.move_layer(lay1, lay2, 'AFTER')
>>> my_map.layers
[Layer(Lay 2), Layer(Lay 1), ....]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
add_basemap

Add a basemap to the Map using the string name from Project.basemaps or a Layer object.

add_data

Add data to the map from a path or URL.

add_layer

Add a layer/table object to the map at the provided position.

add_table

Add a Table to the Map.

clear_selection

Clear all selections in the Map.

clip_to

Clip all layers in the map to the footprint of the input layer

copy_bookmark

Make a copy of the Bookmark with an optional new name.

create_graphics_layer

Create a GraphicsLayer in the Map with the provided name

create_group

Create a new GroupLayer in the Map.

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export_bkmx

Export the Map's bkmx data to a file.

export_mapx

Export the Map's mapx representation to a file.

filter
filter_by_type
filter_groups

Filter Layers using a predicate function.

filter_layers

Filter Layers using a predicate function.

filter_tables

Filter Tables using a predicate function.

get_cim

Get the CIM definition for the Element.

import_bkmx

Import Bookmarks from a bkmx file.

layers_by_type

Matched layers must be of all provided types (or none of provided types if inverted)

move_layer

Move a layer to a position (BEFORE/AFTER) relative to the reference Layer.

open_view
refresh

Refresh all cached elements.

remove

Remove a Map Element. (Layer/Table/Bookmark)

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

update_connection

Update data connections in the Map.

ATTRIBUTE DESCRIPTION
all_layers

Get all Layers in the Map (including GroupLayers and broken Layers).

TYPE: ElementList[Layer]

bkmx

Get the bkmx file data as bytes.

TYPE: bytes

bkmx_dict

Get the bkmx file data as a Python dictionary.

TYPE: dict[str, Any]

broken_layers

Get all broken Layers in the Map.

TYPE: ElementList[Layer]

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

camera

Get the Map camera.

TYPE: Camera

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

color_model

Get the current Map ColorModel. (CMYK, RGB)

TYPE: ColorModel

default_view

TYPE: MapView

elem

excluded_from_clipping

Get all Layers in the Map that are excluded from clipping.

TYPE: ElementList[Layer]

group_layers

Get all GroupLayers in the Map.

TYPE: ElementList[GroupLayer]

layers

Get all Layers in the Map (excluding GroupLayers and broken Layers).

TYPE: ElementList[Layer]

map_type

Get the type of the Map. (GLOBE, SCENE, MAP)

TYPE: MapType

mapx

Get the mapx file data as bytes.

TYPE: bytes

mapx_dict

Get the mapx file data as a Python dictionary.

TYPE: dict[str, Any]

metadata

Get the Metadata for the Map.

TYPE: Metadata

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

scale

Get the current reference scale of the Map.

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

spatial_reference

Get the SpatialReference of the Map (or Map._default_reference).

TYPE: SpatialReference

tables

Get all Tables in the Map.

TYPE: ElementList[Table]

transformations

Get a mapping of all transformations present in the Map.

TYPE: dict[str, Any]

unique_name

units

Get the name of the current Map's units.

TYPE: str

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

all_layers property

all_layers: ElementList[Layer]

Get all Layers in the Map (including GroupLayers and broken Layers). By default this is cached on first access.

bkmx property

bkmx: bytes

Get the bkmx file data as bytes.

bkmx_dict property

bkmx_dict: dict[str, Any]

Get the bkmx file data as a Python dictionary.

broken_layers property

broken_layers: ElementList[Layer]

Get all broken Layers in the Map. By default this is cached on first access.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

camera property writable

camera: Camera

Get the Map camera.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

color_model property writable

color_model: ColorModel

Get the current Map ColorModel. (CMYK, RGB)

default_view property

default_view: MapView

elem instance-attribute

elem = cast(MPElem, elem)

excluded_from_clipping property writable

excluded_from_clipping: ElementList[Layer]

Get all Layers in the Map that are excluded from clipping.

group_layers property

group_layers: ElementList[GroupLayer]

Get all GroupLayers in the Map. By default this is cached on first access.

layers property

layers: ElementList[Layer]

Get all Layers in the Map (excluding GroupLayers and broken Layers). By default this is cached on first access.

map_type property

map_type: MapType

Get the type of the Map. (GLOBE, SCENE, MAP)

mapx property

mapx: bytes

Get the mapx file data as bytes.

mapx_dict property

mapx_dict: dict[str, Any]

Get the mapx file data as a Python dictionary.

metadata property writable

metadata: Metadata

Get the Metadata for the Map.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

scale property writable

scale: float

Get the current reference scale of the Map.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

spatial_reference property writable

spatial_reference: SpatialReference

Get the SpatialReference of the Map (or Map._default_reference).

tables property

tables: ElementList[Table]

Get all Tables in the Map. By default this is cached on first access.

transformations property writable

transformations: dict[str, Any]

Get a mapping of all transformations present in the Map.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

units property

units: str

Get the name of the current Map's units.

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

add_basemap

add_basemap(basemap: str | LayerLike) -> None

Add a basemap to the Map using the string name from Project.basemaps or a Layer object.

Source code in src/arcpie/project/elements.py
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
def add_basemap(self, basemap: str | LayerLike) -> None:
    """Add a basemap to the Map using the string name from `Project.basemaps` or a Layer object."""
    if isinstance(basemap, str):
        if not self.parent:
            raise ValueError('Adding basemaps by name only supported on Maps with a parent project')
        if self.parent and basemap in self.parent.basemaps:
            return self.elem.addBasemap(basemap)
        else:
            raise ValueError(f'{basemap} is not in {self.parent.basemaps}')
    if isinstance(basemap, Element):
        basemap = basemap.elem
    self.add_layer(basemap, position='BOTTOM')

add_data

add_data(
    path: FeatureClass,
    service_type: WebServiceType = ...,
    **params: Any,
) -> Layer
add_data(
    path: Table,
    service_type: WebServiceType = ...,
    **params: Any,
) -> Table
add_data(
    path: Path | str,
    service_type: WebServiceType = ...,
    **params: Any,
) -> Layer | Table
add_data(
    path: FeatureClass | Table | Path | str,
    service_type: WebServiceType = 'AUTOMATIC',
    **params: Any,
) -> Layer | Table

Add data to the map from a path or URL.

PARAMETER DESCRIPTION

path

A filepath or URL for the data element

TYPE: FeatureClass | Table | Path | str

service_type

An optional agument for web service type (default: AUTOMATIC)

TYPE: WebServiceType DEFAULT: 'AUTOMATIC'

**params

Additional keyword parameters passed to the webservice (optional)

TYPE: Any DEFAULT: {}

Source code in src/arcpie/project/elements.py
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
def add_data(self, path: fc.FeatureClass | fc.Table | Path | str, service_type: mpt.WebServiceType = 'AUTOMATIC', **params: Any) -> Layer | Table:
    """Add data to the map from a path or URL.

    Args:
        path: A filepath or URL for the data element
        service_type: An optional agument for web service type (default: `AUTOMATIC`)
        **params: Additional keyword parameters passed to the webservice (optional)
    """
    elem = self.elem.addDataFromPath(str(path), web_service_type=service_type, custom_parameters=params or None)

    elem_name = type(elem).__name__
    if elem_name == 'Layer':
        self.refresh('layers')
        return Layer(cast(mpt.Layer, elem), self)
    if elem_name == 'Table':
        self.refresh('tables')
        return Table(cast(mpt.Table, elem), self)

    # Unreachable ? (at least not documented as reachable...)
    raise ValueError(f'Something went wrong got {type(elem)} but expected Layer or Table')

add_layer

add_layer(
    layer: LayerLike,
    *,
    before: LayerLike | None = ...,
    after: LayerLike | None = ...,
    position: AddPosition = ...,
    group: GroupLayerLike | None = ...,
) -> Layer
add_layer(
    layer: GroupLayer,
    *,
    before: LayerLike | None = ...,
    after: LayerLike | None = ...,
    position: AddPosition = ...,
    group: GroupLayerLike | None = ...,
) -> GroupLayer
add_layer(
    layer: LayerLike | GroupLayerLike,
    *,
    before: LayerLike | None = None,
    after: LayerLike | None = None,
    position: AddPosition = 'AUTO_ARRANGE',
    group: GroupLayerLike | None = None,
) -> Layer | GroupLayer

Add a layer/table object to the map at the provided position.

PARAMETER DESCRIPTION

layer

The layer or table object to add (either arcpie or arcpy versions will work)

TYPE: LayerLike | GroupLayerLike

before

Insert the layer before this layer

TYPE: LayerLike | None DEFAULT: None

after

Insert the layer after this layer

TYPE: LayerLike | None DEFAULT: None

position

The position in the contents pane to add the layer to (default: AUTO_ARRANGE)

TYPE: AddPosition DEFAULT: 'AUTO_ARRANGE'

group

An optional GroupLayer to add the new layer to

TYPE: GroupLayerLike | None DEFAULT: None

Note

before, after, and group + position are exclusive. before takes precedence over all other arguments followed by after, then position and group

Source code in src/arcpie/project/elements.py
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
def add_layer(self, layer: LayerLike | GroupLayerLike,
              *,
              before: LayerLike | None = None,
              after: LayerLike | None = None,
              position: mpt.AddPosition = 'AUTO_ARRANGE',
              group: GroupLayerLike | None = None) -> Layer | GroupLayer:
    """Add a layer/table object to the map at the provided position.

    Args:
        layer: The layer or table object to add (either arcpie or arcpy versions will work)
        before: Insert the layer before this layer
        after: Insert the layer after this layer
        position: The position in the contents pane to add the layer to (default: `AUTO_ARRANGE`)
        group: An optional GroupLayer to add the new layer to

    Note:
        `before`, `after`, and `group` + `position` are exclusive.
        `before` takes precedence over all other arguments followed by `after`, then `position` and `group`
    """
    is_group = isinstance(layer, GroupLayer)
    layer = layer.elem if isinstance(layer, Element) else layer
    group = group.elem if isinstance(group, Element) else group
    before = before.elem if isinstance(before, Element) else before
    after = after.elem if isinstance(after, Element) else after

    if before:
        elem = self.elem.insertLayer(before, layer, 'BEFORE')
    elif after:
        elem = self.elem.insertLayer(after, layer, 'AFTER')
    elif group:
        elem = self.elem.addLayerToGroup(group, layer, position)[0]
    else:
        elem = self.elem.addLayer(layer, position)[0]

    if type(elem).__name__ != 'Layer':
        raise ValueError(f'Expected Layer object, got {type(elem)}. use `add_table` to add Tables.')

    if not is_group:
        self.refresh('layers')
        return Layer(cast(mpt.Layer, elem), self)
    else:
        self.refresh('group_layers')
        return GroupLayer(cast(mpt.Layer, elem), self)

add_table

add_table(
    table: TableLike, *, group: GroupLayerLike | None = None
) -> Table

Add a Table to the Map.

PARAMETER DESCRIPTION

table

The table to add.

TYPE: TableLike

group

An optional GroupLayer to add the Table to.

TYPE: GroupLayerLike | None DEFAULT: None

Source code in src/arcpie/project/elements.py
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
def add_table(self, table: TableLike,
              *,
              group: GroupLayerLike | None = None) -> Table:
    """Add a Table to the Map.

    Args:
        table: The table to add.
        group: An optional GroupLayer to add the Table to.
    """
    if isinstance(table, Table):
        table = table.elem
    if isinstance(group, GroupLayer):
        group = group.elem
    elem = (
        self.elem.addTableToGroup(group, table)
        if group is not None
        else self.elem.addTable(table)
    )
    if not isinstance(elem, mp.Table):
        raise ValueError(f'Expected Table object, got {type(elem)}. use `add_layer` to add Layers.')
    self.refresh('tables')
    return Table(elem, self)

clear_selection

clear_selection() -> None

Clear all selections in the Map.

Source code in src/arcpie/project/elements.py
1972
1973
1974
def clear_selection(self) -> None:
    """Clear all selections in the Map."""
    self.elem.clearSelection()

clip_to

clip_to(layer: LayerLike, selected: bool = False) -> None

Clip all layers in the map to the footprint of the input layer

PARAMETER DESCRIPTION

layer

The Layer to clip to.

TYPE: LayerLike

selected

Clip only to the selected features. (default: False)

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
def clip_to(self, layer: LayerLike, selected: bool = False) -> None:
    """Clip all layers in the map to the footprint of the input layer

    Args:
        layer: The Layer to clip to.
        selected: Clip only to the selected features. (default: `False`)
    """
    if isinstance(layer, Layer):
        layer = layer.elem
    self.elem.clipLayers(layer, selection='SELECTED' if selected else 'ALL')

copy_bookmark

copy_bookmark(
    bookmark: BookmarkLike, name: str | None = None
) -> Bookmark

Make a copy of the Bookmark with an optional new name.

Source code in src/arcpie/project/elements.py
1976
1977
1978
1979
def copy_bookmark(self, bookmark: BookmarkLike, name: str | None = None) -> Bookmark:
    """Make a copy of the Bookmark with an optional new name."""
    bookmark = bookmark.elem if isinstance(bookmark, Element) else bookmark
    return Bookmark(self.elem.copyBookmark(bookmark, name), self)

create_graphics_layer

create_graphics_layer(name: str) -> Layer

Create a GraphicsLayer in the Map with the provided name

Source code in src/arcpie/project/elements.py
1968
1969
1970
def create_graphics_layer(self, name: str) -> Layer:
    """Create a GraphicsLayer in the Map with the provided name"""
    return Layer(self.elem.createGraphicsLayer(name), self)

create_group

create_group(
    name: str, *, parent: GroupLayerLike | None = None
) -> GroupLayer

Create a new GroupLayer in the Map.

PARAMETER DESCRIPTION

name

The name of the new GroupLayer.

TYPE: str

parent

An optional parent group for the new layer.

TYPE: GroupLayerLike | None DEFAULT: None

Source code in src/arcpie/project/elements.py
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
def create_group(self, name: str,
                 *,
                 parent: GroupLayerLike | None = None) -> GroupLayer:
    """Create a new GroupLayer in the Map.

    Args:
        name: The name of the new GroupLayer.
        parent: An optional parent group for the new layer.
    """
    if isinstance(parent, GroupLayer):
        parent = parent.elem
    return GroupLayer(self.elem.createGroupLayer(name, parent), self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export_bkmx

export_bkmx(outfile: Path | str) -> Path

Export the Map's bkmx data to a file.

Source code in src/arcpie/project/elements.py
2039
2040
2041
2042
2043
2044
def export_bkmx(self, outfile: Path | str) -> Path:
    """Export the Map's `bkmx` data to a file."""
    outfile = Path(outfile).with_suffix('.bkmx')
    with outfile.open('wb') as bkmx:
        bkmx.write(self.bkmx)
    return outfile

export_mapx

export_mapx(outfile: Path | str) -> Path

Export the Map's mapx representation to a file.

Source code in src/arcpie/project/elements.py
2032
2033
2034
2035
2036
2037
def export_mapx(self, outfile: Path | str) -> Path:
    """Export the Map's `mapx` representation to a file."""
    outfile = Path(outfile).with_suffix('.mapx')
    with outfile.open('wb') as mapx:
        mapx.write(self.mapx)
    return outfile

filter

filter(
    pred: Callable[[Layer | Table | GroupLayer], bool],
) -> ElementList[Layer | Table | GroupLayer]
Source code in src/arcpie/project/elements.py
2056
2057
def filter(self, pred: Callable[[Layer | Table | GroupLayer], bool]) -> ElementList[Layer | Table | GroupLayer]:
    return self.layers.filter(pred) + self.tables.filter(pred) + self.group_layers.filter(pred)

filter_by_type

filter_by_type(
    include: Iterable[LayerType] | None = None,
    exclude: Iterable[LayerType] | None = None,
) -> ElementList[Layer]
Source code in src/arcpie/project/elements.py
1850
1851
1852
1853
1854
1855
def filter_by_type(self, include: Iterable[LayerType] | None = None, exclude: Iterable[LayerType] | None = None) -> ElementList[Layer]:
    include = set(include or [])
    exclude = set(exclude or [])
    included = self.layers_by_type(*include)
    excluded = self.layers_by_type(*exclude, invert=True)
    return ElementList(lay for lay in included if lay not in excluded)

filter_groups

filter_groups(
    pred: Callable[[GroupLayer], bool],
) -> ElementList[GroupLayer]

Filter Layers using a predicate function.

Source code in src/arcpie/project/elements.py
2067
2068
2069
def filter_groups(self, pred: Callable[[GroupLayer], bool]) -> ElementList[GroupLayer]:
    """Filter Layers using a predicate function."""
    return self.group_layers.filter(pred)

filter_layers

filter_layers(
    pred: Callable[[Layer], bool],
) -> ElementList[Layer]

Filter Layers using a predicate function.

Source code in src/arcpie/project/elements.py
2059
2060
2061
def filter_layers(self, pred: Callable[[Layer], bool]) -> ElementList[Layer]:
    """Filter Layers using a predicate function."""
    return self.layers.filter(pred)

filter_tables

filter_tables(
    pred: Callable[[Table], bool],
) -> ElementList[Table]

Filter Tables using a predicate function.

Source code in src/arcpie/project/elements.py
2063
2064
2065
def filter_tables(self, pred: Callable[[Table], bool]) -> ElementList[Table]:
    """Filter Tables using a predicate function."""
    return self.tables.filter(pred)

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

import_bkmx

import_bkmx(bkmx: Path | str | bytes) -> None

Import Bookmarks from a bkmx file.

Source code in src/arcpie/project/elements.py
2046
2047
2048
2049
2050
2051
2052
2053
2054
def import_bkmx(self, bkmx: Path | str | bytes) -> None:
    """Import Bookmarks from a `bkmx` file."""
    if isinstance(bkmx, bytes):
        with tempfile.TemporaryDirectory() as tmp:
            tmp = (Path(tmp) / '_bookmarks').with_suffix('.bkmx')
            tmp.write_bytes(bkmx)
            self.elem.importBookmarks(str(tmp))
    else:
        self.elem.importBookmarks(str(bkmx))

layers_by_type

layers_by_type(
    *types: LayerType, invert: bool = False
) -> ElementList[Layer]

Matched layers must be of all provided types (or none of provided types if inverted)

Source code in src/arcpie/project/elements.py
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
def layers_by_type(self, *types: LayerType, invert: bool = False) -> ElementList[Layer]:
    """Matched layers must be of all provided types (or none of provided types if inverted)"""
    types_set = set(types)
    return ElementList(
        lay for lay in self.all_layers
        if (
            lay.types.issuperset(types_set)
            if not invert
            else not lay.types.issuperset(types_set)
        )
    )

move_layer

move_layer(
    layer: LayerLike | GroupLayerLike,
    reference: LayerLike | GroupLayerLike,
    position: MovePosition = 'BEFORE',
) -> None

Move a layer to a position (BEFORE/AFTER) relative to the reference Layer.

Source code in src/arcpie/project/elements.py
1915
1916
1917
1918
1919
def move_layer(self, layer: LayerLike | GroupLayerLike, reference: LayerLike | GroupLayerLike, position: mpt.MovePosition = 'BEFORE') -> None:
    """Move a layer to a position (`BEFORE`/`AFTER`) relative to the reference Layer."""
    layer = layer.elem if isinstance(layer, Element) else layer
    reference = reference.elem if isinstance(reference, Element) else reference
    self.elem.moveLayer(reference, layer, position)

open_view

open_view() -> None
Source code in src/arcpie/project/elements.py
2071
2072
def open_view(self) -> None:
    self.elem.openView()

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

remove

remove(child: LayerLike | TableLike | BookmarkLike) -> None

Remove a Map Element. (Layer/Table/Bookmark)

Source code in src/arcpie/project/elements.py
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
def remove(self, child: LayerLike | TableLike | BookmarkLike) -> None:
    """Remove a Map Element. (Layer/Table/Bookmark)"""
    child = child.elem if isinstance(child, Element) else child
    child_type = type(child).__name__
    {
        'Layer': self.elem.removeLayer,
        'Table': self.elem.removeTable,
        'Bookmark': self.elem.removeBookmark,
    }.get(child_type, _noop)(child)  # type: ignore
    self.refresh(child_type.lower() + 's')

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

update_connection

update_connection(
    new: Path | str,
    current: Path | str | None = None,
    auto_update: bool = True,
    validate: bool = True,
    ignore_case: bool = False,
) -> None

Update data connections in the Map.

PARAMETER DESCRIPTION

new

The new connection path.

TYPE: Path | str

current

The current connection path.

TYPE: Path | str | None DEFAULT: None

auto_update

Update all existing joins and relates.

TYPE: bool DEFAULT: True

validate

Only complete the connection change if the new path is valid.

TYPE: bool DEFAULT: True

ignore_case

Ignore case in queries.

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
def update_connection(self, new: Path | str, current: Path | str | None = None, auto_update: bool = True, validate: bool = True, ignore_case: bool = False) -> None:
    """Update data connections in the Map.

    Args:
        new: The new connection path.
        current: The current connection path.
        auto_update: Update all existing joins and relates.
        validate: Only complete the connection change if the new path is valid.
        ignore_case: Ignore case in queries.
    """
    self.elem.updateConnectionProperties(str(current), str(new), auto_update, validate, ignore_case)
    self.refresh()

MapFrame

MapFrame(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[MapFrame, CIMMapFrame, Layout]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
add_grid

Add a grid or graticule to the MapFrame.

convert_grid

Convert a MapFrame grid into real geometry in the associated Map.

create_bookmark
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export

Export the MapFrame. (wrapper for MapView.export)

get_cim

Get the CIM definition for the Element.

layer_extent

Get the extent of a layer within the MapFrame view.

pan_to

Pan the MapFrame view to an object or extent.

refresh

Refresh all cached elements.

remove_grids

Remove grids from the MapFrame.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

zoom_all

Zoom the MapFrame to fit all Map layers.

zoom_to

Zoom the MapFrame to a Layer/Bookmark/Extent (or extent having object).

ATTRIBUTE DESCRIPTION
alt_text

The hover text for the MapFrame.

TYPE: str

anchor

The anchor point of the MapFrame.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

camera

The current camera of the MapFrame.

TYPE: Camera

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

has_map

Check if the MapFrame has an associated Map

TYPE: bool

height

The height of the MapFrame in page units.

TYPE: float

in_group

Determine if the MapFrame is part of a GroupElement.

TYPE: bool

locked

Editability status of the MapFrame in the Layout.

TYPE: bool

map

The Map associated with the MapFrame.

TYPE: Map

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

parent_group

The GroupElement the MapFrame is a child of if it exists. (raises: AttributeError)

TYPE: GroupElement

rotation

The rotation of the MapFrame in degrees (use camera.heading for view rotation).

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

visible

Visibility state of the MapFrame in the Layout.

TYPE: bool

width

The width of the MapFrame in page units.

TYPE: float

x

TYPE: float

y

The Y coordinate of the anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

alt_text property writable

alt_text: str

The hover text for the MapFrame.

anchor property writable

anchor: Anchor

The anchor point of the MapFrame.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

camera property writable

camera: Camera

The current camera of the MapFrame.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

has_map property

has_map: bool

Check if the MapFrame has an associated Map

height property writable

height: float

The height of the MapFrame in page units.

in_group property

in_group: bool

Determine if the MapFrame is part of a GroupElement.

locked property writable

locked: bool

Editability status of the MapFrame in the Layout.

map property

map: Map

The Map associated with the MapFrame.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

parent_group property

parent_group: GroupElement

The GroupElement the MapFrame is a child of if it exists. (raises: AttributeError)

rotation property writable

rotation: float

The rotation of the MapFrame in degrees (use camera.heading for view rotation).

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the MapFrame in the Layout.

width property writable

width: float

The width of the MapFrame in page units.

x property writable

x: float

y property writable

y: float

The Y coordinate of the anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

add_grid

Add a grid or graticule to the MapFrame.

PARAMETER DESCRIPTION

style_item

The style item to create the grid with (must be a GRID class style).

TYPE: StyleItemLike

Source code in src/arcpie/project/elements.py
3875
3876
3877
3878
3879
3880
3881
3882
def add_grid(self, style_item: StyleItemLike):
    """Add a grid or graticule to the MapFrame.

    Args:
        style_item: The style item to create the grid with (must be a `GRID` class style).
    """
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    self.elem.addGrid(style_item)

convert_grid

convert_grid(
    grid: str,
    out_gdb: Path | str,
    new_name: str | None = None,
) -> None

Convert a MapFrame grid into real geometry in the associated Map.

PARAMETER DESCRIPTION

grid

The name of the grid to export.

TYPE: str

out_gdb

The geodatabase to export the grid features to.

TYPE: Path | str

new_name

The name of the group layer that the features will be added to (default: grid name).

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3892
3893
3894
3895
3896
3897
3898
3899
3900
def convert_grid(self, grid: str, out_gdb: Path | str, new_name: str | None = None) -> None:
    """Convert a MapFrame grid into real geometry in the associated Map.

    Args:
        grid: The name of the grid to export.
        out_gdb: The geodatabase to export the grid features to.
        new_name: The name of the group layer that the features will be added to (default: grid name).
    """
    self.elem.convertGridToFeatures(grid, str(out_gdb), new_name)

create_bookmark

create_bookmark(
    name: str | None = None, description: str | None = None
) -> Bookmark
Source code in src/arcpie/project/elements.py
3902
3903
def create_bookmark(self, name: str | None = None, description: str | None = None) -> Bookmark:
    return Bookmark(self.elem.createBookmark(name, description), self.map)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export

export(
    format: Format | ExportFormat | Format,
    *,
    outfile: None = None,
    antialiasing: Antialiasing | None = ...,
) -> bytes
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str = ...,
    antialiasing: Antialiasing | None = ...,
) -> Path
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str | None = None,
    antialiasing: Antialiasing | None = None,
) -> Path | bytes

Export the MapFrame. (wrapper for MapView.export)

PARAMETER DESCRIPTION

format

A Format object or a string format (string option will use defaults)

TYPE: Format | ExportFormat | Format

outfile

An optional output file location to use (will override format filePath)

TYPE: Path | str | None DEFAULT: None

antialiasing

Antialiasing options for the output file

TYPE: Antialiasing | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
def export(self,
           format: mp.Format | mpt.ExportFormat | fmts.Format,
           *,
           outfile: Path | str | None = None,
           antialiasing: mpt.Antialiasing | None = None,
    ) -> Path | bytes:
    """Export the MapFrame. (wrapper for `MapView.export`)

    Args:
        format: A Format object or a string format (string option will use defaults)
        outfile: An optional output file location to use (will override format filePath)
        antialiasing: Antialiasing options for the output file
    """
    display = None
    outfile = Path(outfile) if outfile else None
    if antialiasing:
        display = cast(mpt.DisplayOptions, mp.CreateExportOptions('DISPLAY'))
        display.setAntialiasing(antialiasing)

    if isinstance(format, fmts.Format):
        format = format.fmt

    if type(format).__name__.endswith('Format'):
        suffix = type(format).__name__[:3].lower()
        format = cast(mpt.ExportFormat, format)

    elif isinstance(format, str):
        suffix = format[:3].lower()
        format = mp.CreateExportFormat(format)

    else:
        raise ValueError(f'Unknown format {type(format)} : {format}')

    with tempfile.TemporaryDirectory() as tmp:
        tmp = Path(tmp)
        out = tmp / f'{self.name}.{suffix}'
        format.filePath = str(out)
        self.elem.export(format, display_options=display)
        data = out.read_bytes()
    if outfile:
        outfile.parent.mkdir(exist_ok=True, parents=True)
        outfile.with_suffix(suffix).write_bytes(data)
        return outfile
    else:
        return data

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

layer_extent

layer_extent(
    layer: LayerLike,
    selected: bool = True,
    symbolized: bool = True,
) -> Extent

Get the extent of a layer within the MapFrame view.

PARAMETER DESCRIPTION

layer

The layer to get an extent for (must be in MapFrame map).

TYPE: LayerLike

selected

Get the extent of selected features only.

TYPE: bool DEFAULT: True

symbolized

Get the extent of symbology at current scale, not real shape.

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
def layer_extent(self, layer: LayerLike, selected: bool = True, symbolized: bool = True) -> Extent:
    """Get the extent of a layer within the MapFrame view.

    Args:
        layer: The layer to get an extent for (must be in MapFrame map).
        selected: Get the extent of selected features only.
        symbolized: Get the extent of symbology at current scale, not real shape.
    """
    layer = layer.elem if isinstance(layer, Element) else layer
    return self.elem.getLayerExtent(layer, selected, symbolized)

pan_to

pan_to(extent: Extent | LayerLike | HasExtent) -> None

Pan the MapFrame view to an object or extent.

Source code in src/arcpie/project/elements.py
3976
3977
3978
3979
3980
3981
3982
def pan_to(self, extent: Extent | LayerLike | HasExtent) -> None:
    """Pan the MapFrame view to an object or extent."""
    if type(extent).__name__ == 'Layer':
        extent = self.layer_extent(cast(mpt.Layer, extent))
    if isinstance(extent, HasExtent):
        extent = extent.extent
    self.elem.panToExtent(cast(Extent, extent))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

remove_grids

remove_grids(grid: str | None = None) -> None

Remove grids from the MapFrame.

PARAMETER DESCRIPTION

grid

A wildcard string that uses the Grid name in the contents pane.

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3884
3885
3886
3887
3888
3889
3890
def remove_grids(self, grid: str | None = None) -> None:
    """Remove grids from the MapFrame.

    Args:
        grid: A wildcard string that uses the Grid name in the contents pane.
    """
    self.elem.removeGrids(grid)

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

zoom_all

zoom_all(
    selected: bool = True, symbolized: bool = True
) -> None

Zoom the MapFrame to fit all Map layers.

PARAMETER DESCRIPTION

selected

Zoom to the extent of the selected features only.

TYPE: bool DEFAULT: True

symbolized

Zoom to the extent of the Layer symbologies.

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
3984
3985
3986
3987
3988
3989
3990
3991
def zoom_all(self, selected: bool = True, symbolized: bool = True) -> None:
    """Zoom the MapFrame to fit all Map layers.

    Args:
        selected: Zoom to the extent of the selected features only.
        symbolized: Zoom to the extent of the Layer symbologies.
    """
    self.elem.zoomToAllLayers(selected, symbolized)

zoom_to

zoom_to(elem: LayerLike | BookmarkLike | HasExtent) -> None

Zoom the MapFrame to a Layer/Bookmark/Extent (or extent having object).

Source code in src/arcpie/project/elements.py
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
def zoom_to(self, elem: LayerLike | BookmarkLike | HasExtent) -> None:
    """Zoom the MapFrame to a Layer/Bookmark/Extent (or extent having object)."""
    if isinstance(elem, Element):
        elem = elem.elem

    elem_type = type(elem).__name__
    if elem_type == 'Bookmark':
        self.elem.zoomToBookmark(cast(mpt.Bookmark, elem))
    if elem_type == 'Layer':
        self.camera.setExtent(self.layer_extent(cast(mpt.Layer, elem)))
    else:
        self.camera.setExtent(cast(HasExtent, elem).extent)

MapSeries

MapSeries(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[MapSeries, CIMMapSeries, Layout]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__iter__
__len__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export

Export the MapSeries.

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

reload

Refresh/Reload the mapseries (refresh is shadowed by Element cache control)

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

clip_to_features

Determine if the MapSeries will clip features to the MapFrame.

TYPE: bool

current_page_name

The name of the current MapSeries page.

TYPE: str

current_page_number

The number of the current MapSeries page.

TYPE: int | str

elem

enabled

Determine if the MapSeries is enabled.

TYPE: bool

features

A mapping of page numbers (strings) to feature records.

TYPE: dict[str, dict[str, Any]]

layer

The sereies Layer for the MapSeries

TYPE: Layer

map_frame

The target MapFrame for the MapSeries.

TYPE: MapFrame

mp_type

mp_type_name

name

Name override for MapSeries, set the name with current_page_name

TYPE: str

page_count

Total pages in the MapSeries.

TYPE: int

page_map

A mapping of all page numbers to page names.

TYPE: dict[str, str]

page_name_field

The name field for the MapSeries.

TYPE: str

page_names

String values for all page names in the MapSeries.

TYPE: list[str]

page_numbers

String values for all page numbers in the MapSeries.

TYPE: list[str]

page_range

A range object representing the page numbers (e.g. 10 pages == range(1, 11))

TYPE: range

page_row

Return a dictionary representing the current map page feature.

TYPE: dict[str, Any]

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

clip_to_features property writable

clip_to_features: bool

Determine if the MapSeries will clip features to the MapFrame.

current_page_name property

current_page_name: str

The name of the current MapSeries page.

current_page_number property

current_page_number: int | str

The number of the current MapSeries page.

elem instance-attribute

elem = cast(MPElem, elem)

enabled property writable

enabled: bool

Determine if the MapSeries is enabled.

features property

features: dict[str, dict[str, Any]]

A mapping of page numbers (strings) to feature records.

layer property

layer: Layer

The sereies Layer for the MapSeries

map_frame property

map_frame: MapFrame

The target MapFrame for the MapSeries.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name: str

Name override for MapSeries, set the name with current_page_name

page_count property

page_count: int

Total pages in the MapSeries.

page_map property

page_map: dict[str, str]

A mapping of all page numbers to page names.

page_name_field property

page_name_field: str

The name field for the MapSeries.

page_names property

page_names: list[str]

String values for all page names in the MapSeries.

page_numbers property

page_numbers: list[str]

String values for all page numbers in the MapSeries.

page_range property

page_range: range

A range object representing the page numbers (e.g. 10 pages == range(1, 11))

page_row property

page_row: dict[str, Any]

Return a dictionary representing the current map page feature.

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__iter__

__iter__() -> Iterator[MapSeries]
Source code in src/arcpie/project/elements.py
4267
4268
4269
4270
4271
4272
4273
4274
4275
def __iter__(self) -> Iterator[MapSeries]:
    pages = range(1, self.elem.pageCount + 1)
    current = self.elem.currentPageNumber
    try:
        for page in pages:
            self.elem.currentPageNumber = page
            yield self
    finally:
        self.elem.currentPageNumber = current

__len__

__len__() -> int
Source code in src/arcpie/project/elements.py
4277
4278
def __len__(self) -> int:
    return self.page_count

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
4280
4281
4282
4283
4284
4285
4286
4287
def __repr__(self) -> str:
    return (
        f'{type(self).__name__}('
            f'current_page="{self.name or 'None'}", '
            f'page_count={self.page_count}, '
            f'parent={self.parent}'
        ')'
    )

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export

export(
    format: _MSFormat | _MSFormatAlt | _MSFormatName,
    *,
    out: None = None,
    antialiasing: Antialiasing | None = ...,
    mapseries_options: MapSeriesExportOptions | None = ...,
    custom_pages: str | None = ...,
    multi_file: bool = ...,
    export_pages: ExportPages = ...,
    print_count: bool = ...,
) -> tuple[bytes, ...]
export(
    format: _MSFormat | _MSFormatAlt | _MSFormatName,
    *,
    out: Path | str = ...,
    antialiasing: Antialiasing | None = ...,
    mapseries_options: MapSeriesExportOptions | None = ...,
    custom_pages: str | None = ...,
    multi_file: bool = ...,
    export_pages: ExportPages = ...,
    print_count: bool = ...,
) -> tuple[Path, ...]
export(
    format: _MSFormat | _MSFormatAlt | _MSFormatName,
    *,
    out: Path | str | None = None,
    antialiasing: Antialiasing | None = None,
    mapseries_options: MapSeriesExportOptions | None = None,
    custom_pages: str | None = None,
    multi_file: bool = False,
    export_pages: ExportPages = 'ALL',
    prefix: str | None = None,
    print_count: bool = False,
) -> tuple[Path, ...] | tuple[bytes, ...]

Export the MapSeries.

PARAMETER DESCRIPTION

format

A string name for the output format or a Formatter object.

TYPE: _MSFormat | _MSFormatAlt | _MSFormatName

out

An optional output file for the export.

TYPE: Path | str | None DEFAULT: None

antialiasing

Antialiazing level for the export.

TYPE: Antialiasing | None DEFAULT: None

mapseries_options

MapSeriesExportOptions object (additional kwargs will set this too.)

TYPE: MapSeriesExportOptions | None DEFAULT: None

custom_pages

An optional custom page range string for the export.

TYPE: str | None DEFAULT: None

multi_file

Export the mapseries as multiple files (one per page).

TYPE: bool DEFAULT: False

export_pages

Page export type (All, Current, Selected, etc.)

TYPE: ExportPages DEFAULT: 'ALL'

prefix

A prefix to add to the output files.

TYPE: str | None DEFAULT: None

print_count

When exporting with multi_file enabled, show export status with print/AddMessage.

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
def export(self,
           format: _MSFormat | _MSFormatAlt | _MSFormatName,
           *,
           out: Path | str | None = None,
           antialiasing: mpt.Antialiasing | None = None,
           mapseries_options: mpt.MapSeriesExportOptions | None = None,
           custom_pages: str | None = None,
           multi_file: bool = False,
           export_pages: mpt.ExportPages = 'ALL',
           prefix: str | None = None,
           print_count: bool = False,
    ) -> tuple[Path, ...] | tuple[bytes, ...]:
    """Export the MapSeries.

    Args:
        format: A string name for the output format or a Formatter object.
        out: An optional output file for the export.
        antialiasing: Antialiazing level for the export.
        mapseries_options: MapSeriesExportOptions object (additional kwargs will set this too.)
        custom_pages: An optional custom page range string for the export.
        multi_file: Export the mapseries as multiple files (one per page).
        export_pages: Page export type (All, Current, Selected, etc.)
        prefix: A prefix to add to the output files.
        print_count: When exporting with `multi_file` enabled, show export status with print/AddMessage.
    """
    ms_opts = mapseries_options or mp.CreateExportOptions('MAPSERIES')
    ms_opts = cast(mpt.MapSeriesExportOptions, ms_opts)
    ms_opts.showExportCount = print_count
    if export_pages == 'CUSTOM' or custom_pages:
        if not custom_pages:
            raise ValueError("`export_pages` is set to 'custom', but no `custom_pages` argument given")
        ms_opts.customPages = custom_pages
        ms_opts.setExportPages('CUSTOM')
    elif multi_file:
        ms_opts.setExportFileOptions('MULTIPLE_FILES_PAGE_NUMBER')
    elif export_pages == 'ALL':
        ms_opts.setExportPages('ALL')
    elif export_pages == 'CURRENT':
        ms_opts.setExportPages('CURRENT')
    elif export_pages == 'SELECTED_INDEX_FEATURES':
        ms_opts.setExportPages('SELECTED_INDEX_FEATURES')

    display = None
    out = Path(out) if out else None

    if antialiasing:
        display = cast(mpt.DisplayOptions, mp.CreateExportOptions('DISPLAY'))
        display.setAntialiasing(antialiasing)

    if isinstance(format, fmts.Format):
        format = format.fmt

    if type(format).__name__.endswith('Format'):
        suffix = type(format).__name__[:3].lower()
        format = cast(_MSFormat, format)

    elif isinstance(format, str):
        if format not in _MSFormatName.__args__:
            raise ValueError(f'{self} can only export to {_MSFormatName.__args__}, not {format}')
        # Some type forcing required here for the subset
        suffix = format[:3].lower()
        format = cast(_MSFormat, mp.CreateExportFormat(format))

    else:
        raise ValueError(f'Unknown format {type(format)} : {format}')

    out_name = f'MSPage.{suffix}' if multi_file else f'{self.name}.{suffix}'
    page_names = self.page_names if multi_file else []
    with tempfile.TemporaryDirectory() as tmp:
        tmp = Path(tmp)
        outfl = tmp / out_name
        format.filePath = str(outfl)
        self.elem.export(format, mapseries_export_options=ms_opts, display_options=display)
        pages = tuple(
            (name, fl.read_bytes())
            for name, fl in zip(
                page_names,
                sorted(tmp.glob(f'*.{suffix}'), key=lambda f: f.stat().st_birthtime_ns),
                strict=True
            )
        ) if multi_file else ((out_name, outfl.read_bytes()),)

    # Return the raw page data if an outfile is not specified
    if out is None:
        return tuple(page[1] for page in pages)

    paths = list[Path]()

    # If out is a pdf and there is only one page, write to it
    if len(pages) == 1:
        fl_name, data = pages[0]
        fl_name = f'{prefix or ''}{fl_name}'
        outfl = (
            out
            if out.suffix == f'.{suffix}'
            else (out / fl_name).with_suffix(f'.{suffix}')
        )
        out.write_bytes(data)
        out.parent.mkdir(exist_ok=True, parents=True)
        paths.append(outfl)
        return tuple(paths)

    # If there are multiple pages, write them into the parent folder of out
    for fl_name, data in pages:
        # multi_file will add a prefix name to the file
        pfx, *fl_name = fl_name.split('_', maxsplit=1)
        fl_name = fl_name[0] if fl_name else pfx
        pfx = prefix or ''
        fl_name = f'{pfx}{fl_name}'
        outfl = out / fl_name
        duplicates = 0
        while outfl.exists():
            outfl = outfl.with_stem(f'{outfl.stem}({duplicates + 1})')
            duplicates += 1
        outfl.parent.mkdir(exist_ok=True, parents=True)
        outfl.write_bytes(data)
        paths.append(outfl)

    return tuple(paths)

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

reload

reload() -> None

Refresh/Reload the mapseries (refresh is shadowed by Element cache control)

Source code in src/arcpie/project/elements.py
4263
4264
4265
def reload(self) -> None:
    """Refresh/Reload the mapseries (refresh is shadowed by Element cache control)"""
    self.elem.refresh()

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

MapSurroundElement

MapSurroundElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[MapSurroundElement, CIMMapSurround]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
apply_style

Apply a MAP_SURROUND StyleItem to the MapSurroundElement.

delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

apply_style

apply_style(style_item: StyleItemLike) -> None

Apply a MAP_SURROUND StyleItem to the MapSurroundElement.

Source code in src/arcpie/project/elements.py
4957
4958
4959
4960
4961
4962
def apply_style(self, style_item: StyleItemLike) -> None:
    """Apply a `MAP_SURROUND` StyleItem to the MapSurroundElement."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass != 'MAP_SURROUND':
        raise ValueError(f'{self} can only use `MAP_SURROUND` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

MapView

MapView(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[MapView, CIMMapView, Project]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
create_bookmark

Create a new Bookmark from the current MapView

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export

Export the MapView.

get_cim

Get the CIM definition for the Element.

layer_extent

Get the Extent for a Layer in the MapView.

pan_to

Pan the MapView to an Extent/Layer/Polygon.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

zoom_all

Zoom the MapView to all layers.

zoom_to

Zoom the MapView to a Layer or Bookmark.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

camera

Get the camera of the MapView

TYPE: Camera

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

extent

Get the current extent of the MapView camera

TYPE: Extent

map

Get the Map that the MapView is associated with (parent is actually Project)

TYPE: Map

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

camera property writable

camera: Camera

Get the camera of the MapView

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

extent property

extent: Extent

Get the current extent of the MapView camera

map property

map: Map

Get the Map that the MapView is associated with (parent is actually Project)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

create_bookmark

create_bookmark(
    name: str | None = None, description: str | None = None
) -> Bookmark

Create a new Bookmark from the current MapView

PARAMETER DESCRIPTION

name

An optional name of the Bookmark. (default: Bookmark{n})

TYPE: str | None DEFAULT: None

description

An optional description for the Bookmark.

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
2160
2161
2162
2163
2164
2165
2166
2167
def create_bookmark(self, name: str | None = None, description: str | None = None) -> Bookmark:
    """Create a new Bookmark from the current MapView

    Args:
        name: An optional name of the Bookmark. (default: `Bookmark{n}`)
        description: An optional description for the Bookmark.
    """
    return Bookmark(self.elem.createBookmark(name, description), self.map)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export

export(
    format: Format | ExportFormat | Format,
    *,
    outfile: None = None,
    antialiasing: Antialiasing | None = ...,
) -> bytes
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str = ...,
    antialiasing: Antialiasing | None = ...,
) -> Path
export(
    format: Format | ExportFormat | Format,
    *,
    outfile: Path | str | None = None,
    antialiasing: Antialiasing | None = None,
) -> Path | bytes

Export the MapView.

PARAMETER DESCRIPTION

format

A Format object or a string format (string option will use defaults)

TYPE: Format | ExportFormat | Format

outfile

An optional output file location to use (will override format filePath)

TYPE: Path | str | None DEFAULT: None

antialiasing

Antialiasing options for the output file

TYPE: Antialiasing | None DEFAULT: None

RETURNS DESCRIPTION
Path

If outfile is set

TYPE: Path | bytes

bytes

If outfile is unset

TYPE: Path | bytes

Source code in src/arcpie/project/elements.py
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
def export(self,
           format: mp.Format | mpt.ExportFormat | fmts.Format,
           *,
           outfile: Path | str | None = None,
           antialiasing: mpt.Antialiasing | None = None,
    ) -> Path | bytes:
    """Export the MapView.

    Args:
        format: A Format object or a string format (string option will use defaults)
        outfile: An optional output file location to use (will override format filePath)
        antialiasing: Antialiasing options for the output file

    Returns:
        Path: If outfile is set
        bytes: If outfile is unset
    """
    display = None
    outfile = Path(outfile) if outfile else None
    if antialiasing:
        display = cast(mpt.DisplayOptions, mp.CreateExportOptions('DISPLAY'))
        display.setAntialiasing(antialiasing)

    if isinstance(format, fmts.Format):
        format = format.fmt

    if type(format).__name__.endswith('Format'):
        suffix = type(format).__name__[:3].lower()
        format = cast(mpt.ExportFormat, format)

    elif isinstance(format, str):
        suffix = format[:3].lower()
        format = mp.CreateExportFormat(format)

    else:
        raise ValueError(f'Unknown format {type(format)} : {format}')

    with tempfile.TemporaryDirectory() as tmp:
        tmp = Path(tmp)
        out = tmp / f'{self.name}.{suffix}'
        format.filePath = str(out)
        self.elem.export(format, display_options=display)
        data = out.read_bytes()
    if outfile:
        outfile.parent.mkdir(exist_ok=True, parents=True)
        outfile.with_suffix(suffix).write_bytes(data)
        return outfile
    else:
        return data

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

layer_extent

layer_extent(
    layer: LayerLike,
    selected: bool = True,
    symbolized: bool = True,
) -> Extent

Get the Extent for a Layer in the MapView.

PARAMETER DESCRIPTION

layer

The layer to get an Extent for.

TYPE: LayerLike

selected

Get the extent for only the selected features in the View.

TYPE: bool DEFAULT: True

symbolized

Consider symbology when getting the Extent.

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
def layer_extent(self, layer: LayerLike, selected: bool = True, symbolized: bool = True) -> Extent:
    """Get the Extent for a Layer in the MapView.

    Args:
        layer: The layer to get an Extent for.
        selected: Get the extent for only the selected features in the View.
        symbolized: Consider symbology when getting the Extent.
    """
    layer = layer.elem if isinstance(layer, Element) else layer
    return self.elem.getLayerExtent(layer, selected, symbolized)

pan_to

pan_to(extent: LayerLike | Polygon | Extent) -> None

Pan the MapView to an Extent/Layer/Polygon.

PARAMETER DESCRIPTION

extent

A Layer, Polygon, or Extent to pan to.

TYPE: LayerLike | Polygon | Extent

Source code in src/arcpie/project/elements.py
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
def pan_to(self, extent: LayerLike | Polygon | Extent) -> None:
    """Pan the MapView to an Extent/Layer/Polygon.

    Args:
        extent: A Layer, Polygon, or Extent to pan to.
    """
    if isinstance(extent, Extent):
        extent = extent
    elif isinstance(extent, Polygon):
        extent = extent.extent
    else:
        extent = self.layer_extent(extent)
    self.elem.panToExtent(extent)

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

zoom_all

zoom_all(
    selected: bool = True, symbolized: bool = True
) -> None

Zoom the MapView to all layers.

PARAMETER DESCRIPTION

selected

Zoom to only the selected features.

TYPE: bool DEFAULT: True

symbolized

Consider symbology when zooming.

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
2194
2195
2196
2197
2198
2199
2200
2201
def zoom_all(self, selected: bool = True, symbolized: bool = True) -> None:
    """Zoom the MapView to all layers.

    Args:
        selected: Zoom to only the selected features.
        symbolized: Consider symbology when zooming.
    """
    self.elem.zoomToAllLayers(selected, symbolized)

zoom_to

zoom_to(
    elem: LayerLike | BookmarkLike | Extent | HasExtent,
) -> None

Zoom the MapView to a Layer or Bookmark.

PARAMETER DESCRIPTION

elem

The Layer or Bookmark to zoom to.

TYPE: LayerLike | BookmarkLike | Extent | HasExtent

Source code in src/arcpie/project/elements.py
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
def zoom_to(self, elem: LayerLike | BookmarkLike | Extent | HasExtent) -> None:
    """Zoom the MapView to a Layer or Bookmark.

    Args:
        elem: The Layer or Bookmark to zoom to.
    """
    if isinstance(elem, HasExtent):
        self.camera.setExtent(elem.extent)
        return
    elif isinstance(elem, Element):
        elem = elem.elem

    elem_type = type(elem).__name__
    if elem_type == 'Bookmark':
        self.elem.zoomToBookmark(cast(mpt.Bookmark, elem))
    elif elem_type == 'Layer':
        elem = cast(mpt.Layer, elem)
        self.camera.setExtent(self.layer_extent(elem))

PageQuery

Bases: NamedTuple

ATTRIBUTE DESCRIPTION
fieldName

TYPE: str

match

TYPE: bool

fieldName instance-attribute

fieldName: str

match instance-attribute

match: bool

PictureElement

PictureElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[PictureElement, CIMPictureGraphic]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
alt_text

The hover text of the PictureElement.

TYPE: str

anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

image_data

Get the raw bytes for the source image. Will work with URLs and local filepaths.

TYPE: bytes

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

source

A string representation of the source image path. (filepath or URL)

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

alt_text property writable

alt_text: str

The hover text of the PictureElement.

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

image_data property

image_data: bytes

Get the raw bytes for the source image. Will work with URLs and local filepaths.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

source property writable

source: str

A string representation of the source image path. (filepath or URL)

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

Project

Project(
    aprx: Path | str | Literal['CURRENT'] | None = None,
    *,
    cached: bool = True,
)

Bases: Element[ArcGISProject, CIMGISProject]

ArcGISProject wrapper

ESRI Documentation

A wrapper for the mp.ArcGISProject class that converts all child elements to wrapped elements. All methods of base object can be accessed using project.elem or directly called if they have no implemented wrapper version.

Example
>>> prj = Project(...)
>>> prj.maps
[Map(Map 1), Map(Map 2)]
>>> with prj:
...     prj.maps['Map 1'].unwrap().set_name('My Map')
>>> prj.reopen()
>>> prj.maps
[Map(My Map), Map(Map 2)]
METHOD DESCRIPTION
__enter__
__eq__
__exit__
__getattr__
__hash__
__repr__
add_database
close

Close the Project if is is open (cannot close CURRENT project)

close_views

Close open views for a CURRENT project

copy_item

Create a copy of a Map/Layout/Report element in the Project

create

Create a new Project from scratch.

create_graphic_element

Create a new GraphicElement in the Project.

create_group_element

Create a new GroupElement in the Project.

create_layout

Create a new Layout in the Project.

create_map

Create a new Map in the Project.

create_picture_element

Create a new PictureElement in the Project.

create_predefined_graphics_element

Create a new GraphicElement in the Project using a predefined style.

create_report

Create a new Report in the Project.

create_text_element

Create a new TextElement in the Project.

delete

Delete the project.

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

from_directory

Create an aprx file from a previously unzipped directory (see: Project.to_directory)

get_cim

Get the CIM definition for the Element.

import_document

Import a document file into this project using ArcGISProject.importDocument.

import_mapx

Import a .mapx file. (see: Project.import_document)

import_mxd

Import a .mxd file. (see: Project.import_document)

import_pagx

Import a .pagx file. (see: Project.import_document)

import_rptx

Import a .rptx file. (see: Project.import_document)

open

Open the project if it is not open (stores an ArcGISProject instance in elem)

read_cim_file

Open the aprx and read a file inside it (useful for extracting images).

refresh

Refresh all cached elements.

reopen

Close and re-open a Project

save

Save the project.

save_as

Save a copy of the project

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

start

Open the project using os.startfile

to_directory

Unzip the aprx file to a target directory.

update_connection

Update a connection at the Project level

ATTRIBUTE DESCRIPTION
active_map

The active map for the Project. Check for active map using has_active_map to avoid AttributeError

TYPE: Map

active_view

The active map for the View. Check for active map using has_active_view to avoid AttributeError

TYPE: MapView | Layout | Report

aprx

Alias for elem

TYPE: ArcGISProject

basemaps

Get the basemap names for the Project

TYPE: list[str]

cache

cache_enabled

children

cim

Load the CIM data from the GISProject.json file in the aprx zip directory

TYPE: CIMGISProject

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

color_ramps

Get the color ramps in the project

TYPE: list[ColorRamp]

databases

Get a list of all arcpie.Dataset objects in the Project.

TYPE: list[Dataset]

date_saved

The datetime the project was last saved

TYPE: datetime

default_database

Get an arcpie.Dataset object for the default project database

TYPE: Dataset

elem

TYPE: ArcGISProject

folder_connections

A list of Folder Connections in the Project

TYPE: list[FolderConf]

has_active_map

Check to see if the Project has an active map set

TYPE: bool

has_active_view

Check to see if the Project has an active view set

TYPE: bool

home

A Path object pointed at the Project Home Folder

TYPE: Path

index

TYPE: dict[str, Any]

is_current

TYPE: bool

is_open

TYPE: bool

is_read_only

TYPE: bool

layouts

An ElementList of Layouts in the Project.

TYPE: ElementList[Layout]

maps

An ElementList of Maps in the Project.

TYPE: ElementList[Map]

metadata

Get the metadata object for the Project

TYPE: Metadata

mp_type

mp_type_name

name

The final path component of the Project file including the .aprx suffix

TYPE: str

nodes

TYPE: dict[int, ProjectNode]

parent

path

A Path object pointed at the Project aprx file

TYPE: Path

raw_nodes

TYPE: list[APRXNode]

reports

An ElementList of Reports in the Project.

TYPE: ElementList[Report]

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

styles

An ElementList of Styles in the Project.

TYPE: ElementList[Style]

toolboxes

A list of Toolboxes in the Project.

TYPE: list[ToolboxConf]

unique_name

uri

version

A string representation of the ArcGIS Pro version the project was created/saved in

TYPE: str

Source code in src/arcpie/project/elements.py
738
739
740
741
742
743
744
745
746
747
def __init__(self, aprx: Path | str | Literal['CURRENT'] | None = None, *, cached: bool = True) -> None:
    self._aprx = aprx or 'CURRENT'
    self._is_open = False
    self.elem = None  # type: ignore
    self.open()
    assert self.elem is not None, f'Unable to open {aprx}'
    self.elem: mp.ArcGISProject
    super().__init__(self.elem, None)
    self.cache_enabled = cached
    self._is_open = True

active_map property

active_map: Map

The active map for the Project. Check for active map using has_active_map to avoid AttributeError Raises: AttributeError: If the Project has no active map

active_view property writable

active_view: MapView | Layout | Report

The active map for the View. Check for active map using has_active_view to avoid AttributeError Raises: AttributeError: If the Project has no active view

aprx property

aprx: ArcGISProject

Alias for elem

basemaps property

basemaps: list[str]

Get the basemap names for the Project

cache instance-attribute

cache = dict[str, Any]()

cache_enabled instance-attribute

cache_enabled = cached

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMGISProject

Load the CIM data from the GISProject.json file in the aprx zip directory

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

color_ramps property

color_ramps: list[ColorRamp]

Get the color ramps in the project

databases property

databases: list[Dataset]

Get a list of all arcpie.Dataset objects in the Project. By default these are cached on first load

date_saved property

date_saved: datetime

The datetime the project was last saved

default_database property writable

default_database: Dataset

Get an arcpie.Dataset object for the default project database

elem instance-attribute

elem: ArcGISProject

folder_connections property writable

folder_connections: list[FolderConf]

A list of Folder Connections in the Project

has_active_map property

has_active_map: bool

Check to see if the Project has an active map set

has_active_view property

has_active_view: bool

Check to see if the Project has an active view set

home property writable

home: Path

A Path object pointed at the Project Home Folder

index property

index: dict[str, Any]

is_current property

is_current: bool

is_open property

is_open: bool

is_read_only property

is_read_only: bool

layouts property

layouts: ElementList[Layout]

An ElementList of Layouts in the Project. By default this property is cached on first load

maps property

An ElementList of Maps in the Project. By default this property is cached on first load

metadata property writable

metadata: Metadata

Get the metadata object for the Project

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name: str

The final path component of the Project file including the .aprx suffix

nodes property

nodes: dict[int, ProjectNode]

parent instance-attribute

parent = parent

path property

path: Path

A Path object pointed at the Project aprx file

raw_nodes property

raw_nodes: list[APRXNode]

reports property

reports: ElementList[Report]

An ElementList of Reports in the Project. By default this property is cached on first load

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

styles property writable

styles: ElementList[Style]

An ElementList of Styles in the Project. By default this property is cached on first load

toolboxes property writable

toolboxes: list[ToolboxConf]

A list of Toolboxes in the Project.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

version property

version: str

A string representation of the ArcGIS Pro version the project was created/saved in MAJOR.MINOR.PATCH

__enter__

__enter__() -> Self
Source code in src/arcpie/project/elements.py
1562
1563
1564
1565
1566
1567
def __enter__(self) -> Self:
    if not self.is_open:
        self.open()
    if self.is_read_only:
        raise PermissionError(f'{self} is read only')
    return self

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__exit__

__exit__(
    exc_type: type[BaseException] | None,
    exc: BaseException | None,
    tb: TracebackType | None,
) -> bool | None
Source code in src/arcpie/project/elements.py
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc: BaseException | None,
    tb: TracebackType | None,
) -> bool | None:
    if exc:
        self.close()
        raise exc
    else:
        self.save()
        self.close()

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
790
791
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.path})'

add_database

add_database(
    database: Dataset | Path | str, *, default: bool = False
)
Source code in src/arcpie/project/elements.py
1556
1557
1558
1559
1560
def add_database(self, database: Dataset | Path | str, *, default: bool = False):
    current = self.elem.databases
    current.append({'databasePath': str(database), 'isDefaultDatabase': default})
    self.elem.updateDatabases(current)
    self.refresh('databases')

close

close() -> None

Close the Project if is is open (cannot close CURRENT project)

Source code in src/arcpie/project/elements.py
773
774
775
776
777
778
779
780
781
def close(self) -> None:
    """Close the Project if is is open (cannot close `CURRENT` project)"""
    # Can't close a CURRENT project
    if self.is_current or not self.is_open:
        return
    self.elem = _ClosedProject(str(self.path))  # type: ignore
    self.refresh()
    self.children.clear()
    self._is_open = False

close_views

close_views(
    view_type: Literal['ALL'] | ViewType = 'ALL',
    wildcard: str | None = None,
) -> None

Close open views for a CURRENT project

PARAMETER DESCRIPTION

view_type

The type of views to close (default: ALL)

TYPE: Literal['ALL'] | ViewType DEFAULT: 'ALL'

wildcard

A wildcard string that will filter the on the view parent (Map/Report/Layout name)

TYPE: str | None DEFAULT: None

Raises: PermissionError: If the Project is not CURRENT

Source code in src/arcpie/project/elements.py
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
def close_views(self, view_type: Literal['ALL'] | mpt.ViewType = 'ALL', wildcard: str | None = None) -> None:
    """Close open views for a `CURRENT` project

    Args:
        view_type: The type of views to close (default: `ALL`)
        wildcard: A wildcard string that will filter the on the view parent (Map/Report/Layout name)
    Raises:
        PermissionError: If the Project is not `CURRENT`
    """
    if not self.is_current:
        raise PermissionError(f'{self} was not initialized as `CURRENT` and has no views to close')
    if view_type == 'ALL':
        self.elem.closeViews('MAPS_AND_LAYOUTS', wildcard)
        self.elem.closeViews('REPORTS', wildcard)
        self.elem.closeViews('TABLES', wildcard)
    else:
        self.elem.closeViews(view_type, wildcard)

copy_item

copy_item(item: MapLike, name: str | None = ...) -> Map
copy_item(
    item: LayoutLike, name: str | None = ...
) -> Layout
copy_item(
    item: ReportLike, name: str | None = ...
) -> Report
copy_item(
    item: MapLike | LayoutLike | ReportLike,
    name: str | None = None,
) -> Map | Layout | Report

Create a copy of a Map/Layout/Report element in the Project

PARAMETER DESCRIPTION

item

The item to copy

TYPE: MapLike | LayoutLike | ReportLike

name

The new name (default: <original>{n})

TYPE: str | None DEFAULT: None

RAISES DESCRIPTION
TypeError

If the element is not a Map/Layout/Report

Source code in src/arcpie/project/elements.py
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
def copy_item(
    self,
    item: MapLike | LayoutLike | ReportLike,
    name: str | None = None,
) -> Map | Layout | Report:
    """Create a copy of a Map/Layout/Report element in the Project

    Args:
        item: The item to copy
        name: The new name (default: `<original>{n}`)

    Raises:
        TypeError: If the element is not a Map/Layout/Report
    """
    if isinstance(item, Element):
        item = item.elem
    item_type = type(item).__name__
    if item_type not in ('Map', 'Layout', 'Report'):
        raise TypeError(f'{type(item)} cannot be copied!')
    item = self.elem.copyItem(item, name)
    if item_type == 'Map':
        return Map(cast(mpt.Map, item), self)
    elif item_type == 'Layout':
        return Layout(cast(mpt.Layout, item), self)
    elif item_type == 'Report':
        return Report(cast(mpt.Report, item), self)
    else:
        raise Exception('Unreachable')

create classmethod

create(
    path: Path | str,
    name: str,
    *,
    home_folder: Path | str | None = None,
    default_database: Path | str | None = None,
    default_toolbox: Path | str | None = None,
    create_parents: bool = True,
) -> Self

Create a new Project from scratch.

PARAMETER DESCRIPTION

path

The target location for the project home folder.

TYPE: Path | str

name

The name of the project aprx file.

TYPE: str

home_folder

An optional path to the project home folder. (default: path.parent)

TYPE: Path | str | None DEFAULT: None

default_database

An optional path to the project default database. (default: {home_folder}/default.gdb)

TYPE: Path | str | None DEFAULT: None

default_toolbox

An optional path to the project default toolbox. (default: {home_folder}/default.atbx)

TYPE: Path | str | None DEFAULT: None

create_parents

Create the parent directories for the Project if they don't exist. (default: )

TYPE: bool DEFAULT: True

Source code in src/arcpie/project/elements.py
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
@classmethod
def create(
    cls,
    path: Path | str,
    name: str,
    *,
    home_folder: Path | str | None = None,
    default_database: Path | str | None = None,
    default_toolbox: Path | str | None = None,
    create_parents: bool = True,
) -> Self:
    """Create a new Project from scratch.

    Args:
        path: The target location for the project home folder.
        name: The name of the project aprx file.
        home_folder: An optional path to the project home folder. (default: `path.parent`)
        default_database: An optional path to the project default database. (default: `{home_folder}/default.gdb`)
        default_toolbox: An optional path to the project default toolbox. (default: `{home_folder}/default.atbx`)
        create_parents: Create the parent directories for the Project if they don't exist. (default: )
    """
    path = Path(path)
    path.mkdir(exist_ok=True, parents=create_parents)
    aprx = mp.CreateArcGISProject(
        project_path=str(path),
        project_name=name,
        create_parent_folder=create_parents,
        home_folder=str(home_folder) if home_folder else None,
        default_database=str(default_database) if default_database else None,
        default_toolbox=str(default_toolbox) if default_toolbox else None,
    )
    return cls(aprx.filePath)

create_graphic_element

create_graphic_element(
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> GraphicElement

Create a new GraphicElement in the Project.

PARAMETER DESCRIPTION

container

A Layout or GroupElement to create the GraphicElement in.

TYPE: LayoutLike | GroupElementLike

geometry

A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.

TYPE: Point | Polyline | Polygon | HasCentroid

style_item

An optional style to apply to the GraphicElement.

TYPE: StyleItemLike | None DEFAULT: None

name

An optional name to give the GraphicElement. (default: Element{n})

TYPE: str | None DEFAULT: None

lock_aspect_ratio

Lock the element aspect ratio to prevent skewing. (default: True)

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
ValueError

If the container element is not a Layout or a GroupElement

Source code in src/arcpie/project/elements.py
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
def create_graphic_element(
    self,
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> GraphicElement:
    """Create a new GraphicElement in the Project.

    Args:
        container: A Layout or GroupElement to create the GraphicElement in.
        geometry: A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.
        style_item: An optional style to apply to the GraphicElement.
        name: An optional name to give the GraphicElement. (default: `Element{n}`)
        lock_aspect_ratio: Lock the element aspect ratio to prevent skewing. (default: `True`)

    Raises:
        ValueError: If the container element is not a `Layout` or a `GroupElement`
    """
    if isinstance(container, Element):
        container = container.elem
    if isinstance(style_item, Element):
        style_item = style_item.elem
    if not isinstance(geometry, Point | Polyline | Polygon):
        geometry = geometry.centroid
    container_type = type(container).__name__
    if container_type not in ('Layout', 'GroupElement'):
        raise ValueError(
            f'{container_type} cannot be used as container for GraphicElement. '
            'Must be Layout or GroupElement'
        )
    ge = self.elem.createGraphicElement(container, geometry, style_item, name, lock_aspect_ratio)

    if container_type == 'Layout':
        return GraphicElement(ge, Layout(cast(mpt.Layout, container), self))
    elif container_type == 'GroupElement':
        # TODO: Allow Graphic Elements to be parented to GroupElements
        return GraphicElement(ge, None)
    # elif container_type == 'Layer':
    #     # TODO: Allow Graphic Elements to be parented to Layers (Graphic Layers only)
    #     return GraphicElement(ge, None)
    else:
        raise Exception('Unreachable')

create_group_element

create_group_element(
    container: LayoutLike | GroupElementLike,
    elements: Iterable[
        LayoutElement[LayoutElement, Any] | LayoutElement
    ],
    name: str | None,
) -> GroupElement

Create a new GroupElement in the Project.

PARAMETER DESCRIPTION

container

A Layout or GroupElement to create the GroupElement in.

TYPE: LayoutLike | GroupElementLike

elements

An iterable of LayoutElements to include in the group.

TYPE: Iterable[LayoutElement[LayoutElement, Any] | LayoutElement]

name

An optional name to give the GroupElement. (default: Group{n})

TYPE: str | None

RAISES DESCRIPTION
ValueError

If the container element is not a Layout or a GroupElement

Source code in src/arcpie/project/elements.py
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
def create_group_element(
    self,
    container: LayoutLike | GroupElementLike,
    elements: Iterable[LayoutElement[mpt.LayoutElement, Any] | mpt.LayoutElement],
    name: str | None,
) -> GroupElement:
    """Create a new GroupElement in the Project.

    Args:
        container: A Layout or GroupElement to create the GroupElement in.
        elements: An iterable of LayoutElements to include in the group.
        name: An optional name to give the GroupElement. (default: `Group{n}`)

    Raises:
        ValueError: If the container element is not a `Layout` or a `GroupElement`
    """
    if isinstance(container, Element):
        container = container.elem
    container_type = type(container).__name__
    if container_type not in ('Layout', 'GroupElement'):
        raise ValueError(
            f'GroupElement cannot be created in {container_type}, '
            'must be Layout or GroupElement'
        )
    if not elements:
        raise ValueError('New GroupElement requires elements')
    converted_elements = [
        elem if not isinstance(elem, Element) else elem.elem
        for elem in elements
    ]

    ge = self.elem.createGroupElement(container, converted_elements, name)
    if container_type == 'GroupElement':
        return GroupElement(ge, None)
    elif container_type == 'Layout':
        return GroupElement(ge, Layout(cast(mpt.Layout, container), self))
    else:
        raise Exception('Unreachable')

create_layout

create_layout(
    width: float,
    height: float,
    units: PageUnits,
    name: str | None = None,
) -> Layout

Create a new Layout in the Project.

PARAMETER DESCRIPTION

width

The Layout width in units

TYPE: float

height

The Layout height in units

TYPE: float

units

The Layout page units

TYPE: PageUnits

name

An optional name for the Layout (default: Layout{n})

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
def create_layout(self, width: float, height: float, units: mpt.PageUnits, name: str | None = None) -> Layout:
    """Create a new Layout in the Project.

    Args:
        width: The Layout width in `units`
        height: The Layout height in `units`
        units: The Layout page units
        name: An optional name for the Layout (default: `Layout{n}`)
    """
    return Layout(self.elem.createLayout(width, height, units, name), self)

create_map

create_map(
    name: str | None = None, type: MapType = 'MAP'
) -> Map

Create a new Map in the Project.

PARAMETER DESCRIPTION

name

An optional name for the Map (default: Map{n})

TYPE: str | None DEFAULT: None

type

The map type to create (default: MAP)

TYPE: MapType DEFAULT: 'MAP'

Source code in src/arcpie/project/elements.py
1135
1136
1137
1138
1139
1140
1141
1142
def create_map(self, name: str | None = None, type: mpt.MapType = 'MAP') -> Map:
    """Create a new Map in the Project.

    Args:
        name: An optional name for the Map (default: `Map{n}`)
        type: The map type to create (default: `MAP`)
    """
    return Map(self.elem.createMap(name, type), self)

create_picture_element

create_picture_element(
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    image: Path | str | bytes,
    image_extension: str | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> PictureElement

Create a new PictureElement in the Project.

PARAMETER DESCRIPTION

container

A Layout or GroupElement to create the PictureElement in.

TYPE: LayoutLike | GroupElementLike

geometry

A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.

TYPE: Point | Polyline | Polygon | HasCentroid

image

A file path or raw bytes to use as the image.

TYPE: Path | str | bytes

image_extension

If passing raw image bytes, provide an extension (no dot) here. (default: png)

TYPE: str | None DEFAULT: None

name

An optional name to give the PictureElement. (default: Element{n})

TYPE: str | None DEFAULT: None

lock_aspect_ratio

Lock the element aspect ratio to prevent skewing. (default: True)

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
ValueError

If the container element is not a Layout or a GroupElement

Source code in src/arcpie/project/elements.py
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
def create_picture_element(
    self,
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    image: Path | str | bytes,
    image_extension: str | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> PictureElement:
    """Create a new PictureElement in the Project.

    Args:
        container: A Layout or GroupElement to create the PictureElement in.
        geometry: A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.
        image: A file path or raw bytes to use as the image.
        image_extension: If passing raw image bytes, provide an extension (no dot) here. (default: `png`)
        name: An optional name to give the PictureElement. (default: `Element{n}`)
        lock_aspect_ratio: Lock the element aspect ratio to prevent skewing. (default: `True`)

    Raises:
        ValueError: If the container element is not a `Layout` or a `GroupElement`
    """
    if isinstance(container, Element):
        container = container.elem

    container_type = type(container).__name__
    if container_type not in ('Layout', 'GroupElement'):
        raise ValueError(
            f'GroupElement cannot be created in {container_type}, '
            'must be Layout or GroupElement'
        )

    if not isinstance(geometry, Point | Polyline | Polygon):
        geometry = geometry.centroid

    if isinstance(image, bytes):
        with tempfile.TemporaryDirectory() as tmp:
            fl = (Path(tmp) / (name or 'img')).with_suffix(f'.{image_extension or 'png'}')
            fl.write_bytes(image)
            pe = self.elem.createPictureElement(container, geometry, str(fl), name, lock_aspect_ratio)
    else:
        pe = self.elem.createPictureElement(container, geometry, str(image), name, lock_aspect_ratio)

    if container_type == 'GroupElement':
        return PictureElement(pe, None)
    elif container_type == 'Layout':
        return PictureElement(pe, Layout(cast(mpt.Layout, container), self))
    else:
        raise Exception('Unreachable')

create_predefined_graphics_element

create_predefined_graphics_element(
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    shape_type: ShapeType,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> GraphicElement

Create a new GraphicElement in the Project using a predefined style.

PARAMETER DESCRIPTION

container

A Layout or GroupElement to create the GraphicElement in.

TYPE: LayoutLike | GroupElementLike

geometry

A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.

TYPE: Point | Polyline | Polygon | HasCentroid

shape_type

The predefined shape type to create (see: docs).

TYPE: ShapeType

style_item

An optional style item that matches the geometry type provided to the geometry arg.

TYPE: StyleItemLike | None DEFAULT: None

name

An optional name to give the TextElement. (default: Element{n})

TYPE: str | None DEFAULT: None

lock_aspect_ratio

Lock the element aspect ratio to prevent skewing. (default: True)

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
ValueError

If the container element is not a Layout or a GroupElement

Source code in src/arcpie/project/elements.py
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
def create_predefined_graphics_element(
    self,
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    shape_type: mpt.ShapeType,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> GraphicElement:
    """Create a new GraphicElement in the Project using a predefined style.

    Args:
        container: A Layout or GroupElement to create the GraphicElement in.
        geometry: A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.
        shape_type: The predefined shape type to create (see: [docs](https://doc.esri.com/en/arcgis-pro/latest/arcpy/mapping/arcgisproject-class.html#method-createPredefinedGraphicElement)).
        style_item: An optional style item that matches the geometry type provided to the `geometry` arg.
        name: An optional name to give the TextElement. (default: `Element{n}`)
        lock_aspect_ratio: Lock the element aspect ratio to prevent skewing. (default: `True`)

    Raises:
        ValueError: If the container element is not a `Layout` or a `GroupElement`
    """
    if isinstance(container, Element):
        container = container.elem

    container_type = type(container).__name__
    if container_type not in ('Layout', 'GroupElement'):
        raise ValueError(
            f'GroupElement cannot be created in {container_type}, '
            'must be Layout or GroupElement'
        )

    if not isinstance(geometry, Point | Polyline | Polygon):
        geometry = geometry.centroid

    if isinstance(style_item, Element):
        style_item = style_item.elem

    ge = self.elem.createPredefinedGraphicElement(container, geometry, shape_type, style_item, name, lock_aspect_ratio)

    if container_type == 'Layout':
        return GraphicElement(ge, Layout(cast(mpt.Layout, container), self))
    elif container_type == 'GroupElement':
        return GraphicElement(ge, None)
    else:
        raise Exception('Unreachable')

create_report

create_report(
    units: PageUnits,
    margin: Literal['NORMAL', 'NARROW', 'MODERATE', 'WIDE'],
    source: LayerLike | TableLike,
    fields: Iterable[ReportFieldConf],
    stats: Iterable[ReportStatConf],
    name: str | None = None,
    template: Literal[
        'ATTR_LIST',
        'ATTR_LIST_GROUP',
        'BASIC_SUM',
        'BASIC_SUM_GROUP',
        'PAGE_PER_FEATURE',
    ] = 'ATTR_LIST',
    styling: Literal[
        'BLACK_AND_WHITE',
        'COOL_TONES',
        'WARM_TONES',
        'NO_STYLING',
    ] = 'BLACK_AND_WHITE',
) -> Report

Create a new Report in the Project.

PARAMETER DESCRIPTION

units

The Report page units.

TYPE: PageUnits

margin

The Report margin width.

TYPE: Literal['NORMAL', 'NARROW', 'MODERATE', 'WIDE']

source

The Report data source (Layer or Table).

TYPE: LayerLike | TableLike

fields

A list of dictonaries defining the Report fields.

TYPE: Iterable[ReportFieldConf]

stats

A list of dictonaries defining the Report statistics.

TYPE: Iterable[ReportStatConf]

name

An optional name for the Layout. (default: Report{n})

TYPE: str | None DEFAULT: None

template

The Report template. (default: ATTR_LIST)

TYPE: Literal['ATTR_LIST', 'ATTR_LIST_GROUP', 'BASIC_SUM', 'BASIC_SUM_GROUP', 'PAGE_PER_FEATURE'] DEFAULT: 'ATTR_LIST'

styling

The Report color styling. (default: BLACK_AND_WHITE)

TYPE: Literal['BLACK_AND_WHITE', 'COOL_TONES', 'WARM_TONES', 'NO_STYLING'] DEFAULT: 'BLACK_AND_WHITE'

Source code in src/arcpie/project/elements.py
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
def create_report(
    self,
    units: mpt.PageUnits,
    margin: Literal['NORMAL', 'NARROW', 'MODERATE', 'WIDE'],
    source: LayerLike | TableLike,
    fields: Iterable[ReportFieldConf],
    stats: Iterable[ReportStatConf],
    name: str | None = None,
    template: Literal['ATTR_LIST', 'ATTR_LIST_GROUP', 'BASIC_SUM', 'BASIC_SUM_GROUP', 'PAGE_PER_FEATURE'] = 'ATTR_LIST',
    styling: Literal['BLACK_AND_WHITE', 'COOL_TONES', 'WARM_TONES', 'NO_STYLING'] = 'BLACK_AND_WHITE',
) -> Report:
    """Create a new Report in the Project.

    Args:
        units: The Report page units.
        margin: The Report margin width.
        source: The Report data source (Layer or Table).
        fields: A list of dictonaries defining the Report fields.
        stats: A list of dictonaries defining the Report statistics.
        name: An optional name for the Layout. (default: `Report{n}`)
        template: The Report template. (default: `ATTR_LIST`)
        styling: The Report color styling. (default: `BLACK_AND_WHITE`)
    """
    if isinstance(source, Element):
        source = source.elem
    rpt = self.elem.createReport(
        {'units': units, 'margin': margin},
        source,
        list(fields),  # type: ignore (incorrect hint)
        list(stats),  # type: ignore (incorrect hint)
        name,
        template,
        styling
    )
    return Report(rpt, self)

create_text_element

create_text_element(
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    text_type: TextType,
    text: str,
    size: float | None = None,
    font: str | None = None,
    style: str | None = None,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> TextElement

Create a new TextElement in the Project.

PARAMETER DESCRIPTION

container

A Layout or GroupElement to create the TextElement in.

TYPE: LayoutLike | GroupElementLike

geometry

A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.

TYPE: Point | Polyline | Polygon | HasCentroid

text_type

The type of textbox to create.

TYPE: TextType

text

The text to add to the text element.

TYPE: str

size

The font size (in points) of the text.

TYPE: float | None DEFAULT: None

font

The font face to use (must be installed at system level).

TYPE: str | None DEFAULT: None

style

The font style to use (e.g. bold, italic). Options depend on selected font face.

TYPE: str | None DEFAULT: None

style_item

An optional style item that matches the geometry type provided to the geometry arg.

TYPE: StyleItemLike | None DEFAULT: None

name

An optional name to give the TextElement. (default: Element{n})

TYPE: str | None DEFAULT: None

lock_aspect_ratio

Lock the element aspect ratio to prevent skewing. (default: True)

TYPE: bool DEFAULT: True

RAISES DESCRIPTION
ValueError

If the container element is not a Layout or a GroupElement

Source code in src/arcpie/project/elements.py
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
def create_text_element(
    self,
    container: LayoutLike | GroupElementLike,
    geometry: Point | Polyline | Polygon | HasCentroid,
    text_type: mpt.TextType,
    text: str,
    size: float | None = None,
    font: str | None = None,
    style: str | None = None,
    style_item: StyleItemLike | None = None,
    name: str | None = None,
    lock_aspect_ratio: bool = True,
) -> TextElement:
    """Create a new TextElement in the Project.

    Args:
        container: A Layout or GroupElement to create the TextElement in.
        geometry: A Point/Polygon/Polyline or Centroid having object to use as the graphic shape.
        text_type: The type of textbox to create.
        text: The text to add to the text element.
        size: The font size (in points) of the text.
        font: The font face to use (must be installed at system level).
        style: The font style to use (e.g. bold, italic). Options depend on selected font face.
        style_item: An optional style item that matches the geometry type provided to the `geometry` arg.
        name: An optional name to give the TextElement. (default: `Element{n}`)
        lock_aspect_ratio: Lock the element aspect ratio to prevent skewing. (default: `True`)

    Raises:
        ValueError: If the container element is not a `Layout` or a `GroupElement`
    """
    if isinstance(container, Element):
        container = container.elem

    container_type = type(container).__name__
    if container_type not in ('Layout', 'GroupElement'):
        raise ValueError(
            f'GroupElement cannot be created in {container_type}, '
            'must be Layout or GroupElement'
        )

    if not isinstance(geometry, Point | Polyline | Polygon):
        geometry = geometry.centroid

    if isinstance(style_item, Element):
        style_item = style_item.elem

    te = self.elem.createTextElement(container, geometry, text_type, text, size, font, style, style_item, name, lock_aspect_ratio)
    if container_type == 'Layout':
        return TextElement(te, Layout(cast(mpt.Layout, container), self))
    elif container_type == 'GroupElement':
        return TextElement(te, None)
    else:
        raise Exception('Unreachable')

delete

delete(*, home_folder: bool = False) -> None

Delete the project.

PARAMETER DESCRIPTION

home_folder

If set, recursively delete the homeFolder (default: False)

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
def delete(self, *, home_folder: bool = False) -> None:
    """Delete the project.

    Args:
        home_folder: If set, recursively delete the homeFolder (default: `False`)
    """
    if home_folder:
        shutil.rmtree(self.home)
    else:
        self.path.unlink()

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

from_directory classmethod

from_directory(
    directory: Path | str, outfile: Path | str
) -> Project

Create an aprx file from a previously unzipped directory (see: Project.to_directory)

PARAMETER DESCRIPTION

directory

The source directory (see: Project.to_directory).

TYPE: Path | str

outfile

The target aprx file.

TYPE: Path | str

Source code in src/arcpie/project/elements.py
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
@classmethod
def from_directory(cls, directory: Path | str, outfile: Path | str) -> Project:
    """Create an aprx file from a previously unzipped directory (see: `Project.to_directory`)

    Args:
        directory: The source directory (see: `Project.to_directory`).
        outfile: The target `aprx` file.
    """
    directory = Path(directory).resolve()
    outfile = Path(outfile).resolve().with_suffix('.aprx')
    outfile.touch(exist_ok=True)
    with ZipFile(outfile, mode='w', compresslevel=9, compression=ZIP_DEFLATED) as zf:
        for rt, drs, fls in directory.walk():
            for dr in drs:
                zf.mkdir(dr)
            for fl in fls:
                fl = rt / fl
                zf.write(fl, fl.relative_to(directory))
    return cls(outfile)

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

import_document

import_document(
    doc: Path | str | bytes,
    *,
    include_layout: bool = True,
    reuse_existing_maps: bool = True,
    log: bool = True,
    extension: str | None = None,
) -> Layout | Map | Report

Import a document file into this project using ArcGISProject.importDocument.

PARAMETER DESCRIPTION

doc

The path to the document. (.pagx, .mapx, .rptx, .mxd, ...[see importDocument])

TYPE: Path | str | bytes

include_layout

Include layouts with .mapx files.

TYPE: bool DEFAULT: True

reuse_existing_maps

Reuse existing maps with .pagx files.

TYPE: bool DEFAULT: True

log

Log the import in the ImportLog. (default: True)

TYPE: bool DEFAULT: True

extension

If using bytes as the source document, supply the file extension (no dot)

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
def import_document(self, doc: Path | str | bytes,
                    *,
                    include_layout: bool = True,
                    reuse_existing_maps: bool = True,
                    log: bool = True,
                    extension: str | None = None,
    ) -> Layout | Map | Report:
    """Import a document file into this project using `ArcGISProject.importDocument`.

    Args:
        doc: The path to the document. (`.pagx`, `.mapx`, `.rptx`, `.mxd`, ...[see `importDocument`])
        include_layout: Include layouts with `.mapx` files.
        reuse_existing_maps: Reuse existing maps with `.pagx` files.
        log: Log the import in the ImportLog. (default: `True`)
        extension: If using bytes as the source document, supply the file extension (no dot)
    """
    if isinstance(doc, bytes):
        if not extension:
            raise ValueError('Cannot import bytes data without specified extension')
        with tempfile.TemporaryDirectory() as tmp:
            tmp = (Path(tmp) / '_map').with_suffix(f'.{extension}')
            tmp.write_bytes(doc)
            imported: Any = self.elem.importDocument(
                str(doc),
                include_layout=include_layout,
                reuse_existing_maps=reuse_existing_maps,
                log_files=log,
            )
    else:
        doc = Path(doc)
        if doc.suffix not in ('.pagx', '.mapx', '.rptx', '.mxd'):
            raise ValueError(
                f'Document type {doc.suffix} cannot be imported (.pagx, .mapx, .rptx, .mxd)'
            )

        imported: Any = self.elem.importDocument(
            str(doc),
            include_layout=include_layout,
            reuse_existing_maps=reuse_existing_maps,
        )
    imported_type_name = type(imported).__name__
    if imported_type_name == 'Map':
        self.refresh('maps')
        return Map(imported, parent=self)
    if imported_type_name == 'Layout':
        self.refresh('layouts')
        return Layout(imported, parent=self)
    if imported_type_name == 'Report':
        self.refresh('reports')
        return Report(imported, parent=self)
    else:
        raise Exception('Unreachable')

import_mapx

import_mapx(mapx: Path | str | bytes) -> Map

Import a .mapx file. (see: Project.import_document)

Source code in src/arcpie/project/elements.py
1526
1527
1528
1529
1530
1531
1532
1533
1534
def import_mapx(self, mapx: Path | str | bytes) -> Map:
    """Import a `.mapx` file. (see: `Project.import_document`)"""
    if not isinstance(mapx, bytes):
        mapx = Path(mapx)
        if mapx.suffix != '.mapx':
            raise ValueError(f'Expected .mapx file, got {mapx.suffix}')
    imported = self.import_document(mapx, extension='mapx')
    assert isinstance(imported, Map)
    return imported

import_mxd

import_mxd(
    mxd: Path | str | bytes, *, include_layout: bool = True
) -> Map

Import a .mxd file. (see: Project.import_document)

Source code in src/arcpie/project/elements.py
1536
1537
1538
1539
1540
1541
1542
1543
1544
def import_mxd(self, mxd: Path | str | bytes, *, include_layout: bool = True) -> Map:
    """Import a `.mxd` file. (see: `Project.import_document`)"""
    if not isinstance(mxd, bytes):
        mxd = Path(mxd)
        if mxd.suffix != '.mxd':
            raise ValueError(f'Expected .mxd file, got {mxd.suffix}')
    imported = self.import_document(mxd, include_layout=include_layout, extension='mxd')
    assert isinstance(imported, Map)
    return imported

import_pagx

import_pagx(
    pagx: Path | str | bytes,
    *,
    reuse_existing_maps: bool = False,
) -> Layout

Import a .pagx file. (see: Project.import_document)

Source code in src/arcpie/project/elements.py
1516
1517
1518
1519
1520
1521
1522
1523
1524
def import_pagx(self, pagx: Path | str | bytes, *, reuse_existing_maps: bool = False) -> Layout:
    """Import a `.pagx` file. (see: `Project.import_document`)"""
    if not isinstance(pagx, bytes):
        pagx = Path(pagx)
        if pagx.suffix != '.pagx':
            raise ValueError(f'Expected .pagx file, got {pagx.suffix}')
    imported = self.import_document(pagx, reuse_existing_maps=reuse_existing_maps, extension='pagx')
    assert isinstance(imported, Layout)
    return imported

import_rptx

import_rptx(rptx: Path | str | bytes) -> Report

Import a .rptx file. (see: Project.import_document)

Source code in src/arcpie/project/elements.py
1546
1547
1548
1549
1550
1551
1552
1553
1554
def import_rptx(self, rptx: Path | str | bytes) -> Report:
    """Import a `.rptx` file. (see: `Project.import_document`)"""
    if not isinstance(rptx, bytes):
        rptx = Path(rptx)
        if rptx.suffix != '.rptx':
            raise ValueError(f'Expected .rptx file, got {rptx.suffix}')
    imported = self.import_document(rptx, extension='rptx')
    assert isinstance(imported, Report)
    return imported

open

open() -> None

Open the project if it is not open (stores an ArcGISProject instance in elem)

Source code in src/arcpie/project/elements.py
764
765
766
767
768
769
770
771
def open(self) -> None:
    """Open the project if it is not open (stores an `ArcGISProject` instance in `elem`)"""
    # Don't re-initialize a CURRENT project
    if self.is_current and self._is_open:
        return
    prj = mp.ArcGISProject(str(self._aprx))
    self._is_open = True
    self.elem = prj

read_cim_file

read_cim_file(path: Path | str) -> bytes

Open the aprx and read a file inside it (useful for extracting images).

Source code in src/arcpie/project/elements.py
1060
1061
1062
1063
1064
1065
def read_cim_file(self, path: Path | str) -> bytes:
    """Open the aprx and read a file inside it (useful for extracting images)."""
    # ZipFile paths need to be in posix (/ delimited) format
    path = Path(path).as_posix()
    with ZipFile(self.path) as zf:
        return zf.read(str(path))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

reopen

reopen() -> None

Close and re-open a Project

Source code in src/arcpie/project/elements.py
783
784
785
786
787
788
def reopen(self) -> None:
    """Close and re-open a Project"""
    if self.is_current:
        return
    self.close()
    self.open()

save

save() -> None

Save the project.

Source code in src/arcpie/project/elements.py
1415
1416
1417
1418
1419
1420
1421
1422
1423
def save(self) -> None:
    """Save the project.

    Raises:
        ``PermissionError`` if the file is ReadOnly
    """
    if self.is_read_only:
        raise PermissionError(f'{self.name} is read only!')
    self.elem.save()

save_as

save_as(path: Path | str) -> Project

Save a copy of the project

Source code in src/arcpie/project/elements.py
1425
1426
1427
1428
1429
def save_as(self, path: Path | str) -> Project:
    """Save a copy of the project"""
    path = Path(path).with_suffix('.aprx')
    self.elem.saveACopy(str(path))
    return type(self)(path)

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

start

start() -> None

Open the project using os.startfile

Source code in src/arcpie/project/elements.py
1431
1432
1433
def start(self) -> None:
    """Open the project using `os.startfile`"""
    os.startfile(self.path)  # noqa: S606

to_directory

to_directory(
    to: Path | str, *, overwrite: bool = False
) -> Path

Unzip the aprx file to a target directory.

PARAMETER DESCRIPTION

to

The folder to unzip the Project into.

TYPE: Path | str

overwrite

Overwrite the contents of the target folder if it exists.

TYPE: bool DEFAULT: False

RAISES DESCRIPTION
FileExistsError

If to exists and is not an empty directory

Source code in src/arcpie/project/elements.py
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
def to_directory(self, to: Path | str, *, overwrite: bool = False) -> Path:
    """Unzip the aprx file to a target directory.

    Args:
        to: The folder to unzip the Project into.
        overwrite: Overwrite the contents of the target folder if it exists.

    Raises:
        FileExistsError: If `to` exists and is not an empty directory
    """
    to = Path(to).resolve()
    if to.exists() and not overwrite:
        raise FileExistsError(f'{to} exists and `overwrite` is set to `False`')
    with ZipFile(self.path) as zf:
        zf.extractall(to)
    return to

update_connection

update_connection(
    new: str,
    current: str | None = None,
    auto_update: bool = True,
    validate: bool = True,
    ignore_case: bool = False,
)

Update a connection at the Project level

Source code in src/arcpie/project/elements.py
1067
1068
1069
def update_connection(self, new: str, current: str | None = None, auto_update: bool = True, validate: bool = True, ignore_case: bool = False):
    """Update a connection at the Project level"""
    self.elem.updateConnectionProperties(current, new, auto_update, validate, ignore_case)

ProjectNode

Bases: TypedDict

ATTRIBUTE DESCRIPTION
children

TYPE: set[int]

name

TYPE: str

path

TYPE: str

type

TYPE: str

children instance-attribute

children: set[int]

name instance-attribute

name: str

path instance-attribute

path: str

type instance-attribute

type: str

ReferenceDataSource

Bases: TypedDict

ATTRIBUTE DESCRIPTION
connection_info

TYPE: str

dataset

TYPE: str

workspace_factory

TYPE: str

connection_info instance-attribute

connection_info: str

dataset instance-attribute

dataset: str

workspace_factory instance-attribute

workspace_factory: str

Report

Report(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Report, CIMReport, Project]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export

Export the MapSeries.

get_cim

Get the CIM definition for the Element.

open_view

Open the Report in the active Project.

query_as

Context manager for changing the query temporarily.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

definition_query

SQL where clause that the report is filtered with.

TYPE: str

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

report_layout_sections

ElementList of all ReportLayoutSections

TYPE: ElementList[ReportLayoutSection]

report_sections

ElementList of all ReportSecions.

TYPE: ElementList[ReportSection]

sections

ElementList of all ReportSecions and ReportLayoutSections.

TYPE: ElementList[ReportElement[Any, Any]]

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

definition_query property writable

definition_query: str

SQL where clause that the report is filtered with.

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

report_layout_sections property

report_layout_sections: ElementList[ReportLayoutSection]

ElementList of all ReportLayoutSections

report_sections property

report_sections: ElementList[ReportSection]

ElementList of all ReportSecions.

sections property

ElementList of all ReportSecions and ReportLayoutSections.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export

export(
    format: _RPTFormat | _RPTFormatAlt | _RPTFormatName,
    *,
    out: None = None,
    antialiasing: Antialiasing | None = ...,
    report_options: ReportExportOptions | None = ...,
    custom_pages: str | None = ...,
    page_offset: int = ...,
    page_override: int = ...,
    export_pages: ReportExportPages = ...,
) -> tuple[bytes, ...]
export(
    format: _RPTFormat | _RPTFormatAlt | _RPTFormatName,
    *,
    out: Path | str = ...,
    antialiasing: Antialiasing | None = ...,
    report_options: ReportExportOptions | None = ...,
    custom_pages: str | None = ...,
    page_offset: int = ...,
    page_override: int = ...,
    export_pages: ReportExportPages = ...,
) -> tuple[Path, ...]
export(
    format: _RPTFormat | _RPTFormatAlt | _RPTFormatName,
    *,
    out: Path | str | None = None,
    antialiasing: Antialiasing | None = None,
    report_options: ReportExportOptions | None = None,
    custom_pages: str | None = None,
    page_offset: int | None = None,
    page_override: int | None = None,
    export_pages: ReportExportPages = 'ALL',
) -> tuple[Path, ...] | tuple[bytes, ...]

Export the MapSeries.

PARAMETER DESCRIPTION

format

A string name for the output format or a Formatter object.

TYPE: _RPTFormat | _RPTFormatAlt | _RPTFormatName

out

An optional output file for the export.

TYPE: Path | str | None DEFAULT: None

antialiasing

Antialiazing level for the export.

TYPE: Antialiasing | None DEFAULT: None

report_options

ReportExportOptions object (additional kwargs will set this too.)

TYPE: ReportExportOptions | None DEFAULT: None

custom_pages

An optional custom page range string for the export.

TYPE: str | None DEFAULT: None

page_offset

An optional offset on page numbers for the report.

TYPE: int | None DEFAULT: None

page_override

Override the total number of pages.

TYPE: int | None DEFAULT: None

export_pages

The pages to export (Even, Odd, All, etc.)

TYPE: ReportExportPages DEFAULT: 'ALL'

Source code in src/arcpie/project/elements.py
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
def export(self,
           format: _RPTFormat | _RPTFormatAlt | _RPTFormatName,
           *,
           out: Path | str | None = None,
           antialiasing: mpt.Antialiasing | None = None,
           report_options: mpt.ReportExportOptions | None = None,
           custom_pages: str | None = None,
           page_offset: int | None = None,
           page_override: int | None = None,
           export_pages: mpt.ReportExportPages = 'ALL',
    ) -> tuple[Path, ...] | tuple[bytes, ...]:
    """Export the MapSeries.

    Args:
        format: A string name for the output format or a Formatter object.
        out: An optional output file for the export.
        antialiasing: Antialiazing level for the export.
        report_options: ReportExportOptions object (additional kwargs will set this too.)
        custom_pages: An optional custom page range string for the export.
        page_offset: An optional offset on page numbers for the report.
        page_override: Override the total number of pages.
        export_pages: The pages to export (Even, Odd, All, etc.)
    """
    rpt_opts = report_options or mp.CreateExportOptions('REPORT')
    rpt_opts = cast(mpt.ReportExportOptions, rpt_opts)
    export_pages = cast(mpt.ReportExportPages, export_pages.upper())

    if export_pages == 'CUSTOM' or custom_pages:
        if not custom_pages:
            raise ValueError("`export_pages` is set to 'custom', but no `custom_pages` argument given")
        rpt_opts.customPages = custom_pages
        rpt_opts.setExportPages('CUSTOM')
    else:
        rpt_opts.setExportPages(export_pages)
    if page_offset is not None:
        rpt_opts.startingPageNumberLabelOffset = page_offset
    if page_override is not None:
        rpt_opts.totalPageNumberOverride = page_override

    display = None
    out = Path(out) if out else None

    if antialiasing:
        display = cast(mpt.DisplayOptions, mp.CreateExportOptions('DISPLAY'))
        display.setAntialiasing(antialiasing)

    if isinstance(format, fmts.Format):
        format = format.fmt

    if type(format).__name__.endswith('Format'):
        suffix = type(format).__name__[:3].lower()
        format = cast(_RPTFormat, format)

    elif isinstance(format, str):
        if format != 'PDF':
            raise ValueError(f'Report can only export to PDF, not {format}')
        # Some type forcing required here for the subset
        suffix = format[:3].lower()
        format = cast(_RPTFormat, mp.CreateExportFormat(format))

    else:
        raise ValueError(f'Unknown format {type(format)} : {format}')

    out_name = f'{self.name}.{suffix}'

    with tempfile.TemporaryDirectory() as tmp:
        tmp = Path(tmp)
        outfl = tmp / out_name
        format.filePath = str(outfl)
        self.elem.export(
            format,
            report_export_options=rpt_opts,
            display_options=display,
        )
        pages = tuple((fl.name, fl.read_bytes()) for fl in tmp.glob('*.pdf'))

    # Return the raw page data if an outfile is not specified
    if not out:
        return tuple(page[1] for page in pages)

    paths = list[Path]()
    fl_name, data = pages[0]
    outfl = (
        out
        if out.suffix == '.pdf'
        else out.parent / fl_name
    )
    out.write_bytes(data)
    out.parent.mkdir(exist_ok=True, parents=True)
    paths.append(outfl)
    return tuple(paths)

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

open_view

open_view() -> None

Open the Report in the active Project.

Source code in src/arcpie/project/elements.py
4581
4582
4583
def open_view(self) -> None:
    """Open the Report in the active Project."""
    self.elem.openView()

query_as

query_as(query: str | None)

Context manager for changing the query temporarily.

PARAMETER DESCRIPTION

query

The SQL where clause to use as the query.

TYPE: str | None

Example:

>>> for q_name, sql in report_queries.items():
...     with report.query_as(sql):
...         report.export(..., out=q_name)

Source code in src/arcpie/project/elements.py
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
@contextmanager
def query_as(self, query: str | None):
    """Context manager for changing the query temporarily.

    Args:
        query: The SQL where clause to use as the query.

    Example:
    ```python
    >>> for q_name, sql in report_queries.items():
    ...     with report.query_as(sql):
    ...         report.export(..., out=q_name)
    ```
    """
    cur = self.definition_query
    try:
        self.definition_query = query
        yield self
    finally:
        self.definition_query = cur

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ReportElement

ReportElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[MPElem, CIM, Report]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

visible

Visibility state of the ReportElement.

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the ReportElement.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ReportFieldConf

Bases: TypedDict

ATTRIBUTE DESCRIPTION
fieldName

TYPE: str

groupField

TYPE: bool

sortInfo

TYPE: Literal['ASC', 'DESC', 'NONE']

fieldName instance-attribute

fieldName: str

groupField instance-attribute

groupField: bool

sortInfo instance-attribute

sortInfo: Literal['ASC', 'DESC', 'NONE']

ReportLayoutSection

ReportLayoutSection(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: ReportElement[ReportLayoutSection, CIMReportLayoutPageSection]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

visible

Visibility state of the ReportElement.

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the ReportElement.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

ReportSection

ReportSection(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: ReportElement[ReportSection, CIMReportSection]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

query_as

Create a query context for the ReportSection.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

set_report_source

Set the related Report data source.

set_source

Set the ReportSection data source to something that has data.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

definition_query

SQL where clause that filters the ReportSection.

TYPE: str

elem

fields

A list of ReportSectionFields dictionaries for the ReportSection.

TYPE: list[ReportSectionField]

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

source

A ReferenceDataSource dictionary that defines the data connection.

TYPE: ReferenceDataSource

statistics

A list of ReportStatistic dictionaries for the ReportSection.

TYPE: list[ReportStatistic]

unique_name

uri

visible

Visibility state of the ReportElement.

TYPE: bool

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

definition_query property writable

definition_query: str

SQL where clause that filters the ReportSection.

elem instance-attribute

elem = cast(MPElem, elem)

fields property writable

A list of ReportSectionFields dictionaries for the ReportSection.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

source property

A ReferenceDataSource dictionary that defines the data connection.

statistics property

statistics: list[ReportStatistic]

A list of ReportStatistic dictionaries for the ReportSection.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state of the ReportElement.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

query_as

query_as(query: str | None)

Create a query context for the ReportSection.

PARAMETER DESCRIPTION

query

SQL where clause to set for the context block.

TYPE: str | None

Example:

>>> for q_name, sql in section_queries.items():
...     with report_section.query_as(sql):
...         report.export(..., name=q_name)

Source code in src/arcpie/project/elements.py
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
@contextmanager
def query_as(self, query: str | None):
    """Create a query context for the ReportSection.

    Args:
        query: SQL where clause to set for the context block.

    Example:
    ```python
    >>> for q_name, sql in section_queries.items():
    ...     with report_section.query_as(sql):
    ...         report.export(..., name=q_name)
    ```
    """
    cur = self.definition_query
    try:
        self.definition_query = query
        yield self
    finally:
        self.definition_query = cur

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

set_report_source

set_report_source(
    source: LayerLike
    | TableLike
    | FeatureClass
    | Table
    | Path
    | str,
    name: str | None = None,
    rel_class: str | None = None,
)

Set the related Report data source.

PARAMETER DESCRIPTION

source

Any valid data source for a Report.

TYPE: LayerLike | TableLike | FeatureClass | Table | Path | str

name

The name of the datasource.

TYPE: str | None DEFAULT: None

rel_class

The name of the new relationship class.

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
def set_report_source(self, source: LayerLike | TableLike | fc.FeatureClass | fc.Table | Path | str, name: str | None = None, rel_class: str | None = None):
    """Set the related Report data source.

    Args:
        source: Any valid data source for a Report.
        name: The name of the datasource.
        rel_class: The name of the new relationship class.
    """
    if isinstance(source, Element):
        source = source.elem
    else:
        source = str(source)
    self.elem.setRelatedReportSource(source, name, rel_class)

set_source

set_source(
    source: LayerLike
    | TableLike
    | FeatureClass
    | Table
    | Path
    | str,
)

Set the ReportSection data source to something that has data.

PARAMETER DESCRIPTION

source

Any valid source object (Layer, Table, FeatureClass, URL, Path, etc.)

TYPE: LayerLike | TableLike | FeatureClass | Table | Path | str

Source code in src/arcpie/project/elements.py
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
def set_source(self, source: LayerLike | TableLike | fc.FeatureClass | fc.Table | Path | str):
    """Set the ReportSection data source to something that has data.

    Args:
        source: Any valid source object (Layer, Table, FeatureClass, URL, Path, etc.)
    """
    if isinstance(source, Element):
        source = source.elem
    else:
        source = str(source)
    self.elem.setReferenceDataSource(source)

ReportSectionField

Bases: TypedDict

ATTRIBUTE DESCRIPTION
fieldName

TYPE: str

groupField

TYPE: bool

sortOrder

TYPE: Literal['ASC', 'DESC', 'NONE']

fieldName instance-attribute

fieldName: str

groupField instance-attribute

groupField: bool

sortOrder instance-attribute

sortOrder: Literal['ASC', 'DESC', 'NONE']

ReportStatConf

Bases: TypedDict

ATTRIBUTE DESCRIPTION
fieldName

TYPE: str

statistic

TYPE: Literal['COUNT', 'MEAN', 'SUM', 'STD_DEV', 'MAX', 'MIN']

fieldName instance-attribute

fieldName: str

statistic instance-attribute

statistic: Literal[
    'COUNT', 'MEAN', 'SUM', 'STD_DEV', 'MAX', 'MIN'
]

ReportStatistic

Bases: TypedDict

ATTRIBUTE DESCRIPTION
fieldName

TYPE: str

statistic

TYPE: StatisticType

fieldName instance-attribute

fieldName: str

statistic instance-attribute

statistic: StatisticType

SomeLayerProps

Bases: TypedDict

ATTRIBUTE DESCRIPTION
URI

TYPE: str

brightness

TYPE: int

connectionProperties

TYPE: LayerConnectionConf

contrast

TYPE: int

dataSource

TYPE: str

definitionQuery

TYPE: str

elevation

TYPE: LayerElevation

is3DLayer

TYPE: bool

isBasemapLayer

TYPE: bool

isBroken

TYPE: bool

isFeatureLayer

TYPE: bool

isGraphicsLayer

TYPE: bool

isGroupLayer

TYPE: bool

isNetworkAnalystLayer

TYPE: bool

isNetworkDatasetLayer

TYPE: bool

isParcelFabricLayer

TYPE: bool

isRasterLayer

TYPE: bool

isSceneLayer

TYPE: bool

isTimeEnabled

TYPE: bool

isWebLayer

TYPE: bool

longName

TYPE: str

maxThreshold

TYPE: float

metadata

TYPE: Metadata

minThreshold

TYPE: float

name

TYPE: str

showLabels

TYPE: bool

symbology

TYPE: Symbology

time

TYPE: LayerTime

transparency

TYPE: int

visible

TYPE: bool

URI instance-attribute

URI: str

brightness instance-attribute

brightness: int

connectionProperties instance-attribute

connectionProperties: LayerConnectionConf

contrast instance-attribute

contrast: int

dataSource instance-attribute

dataSource: str

definitionQuery instance-attribute

definitionQuery: str

elevation instance-attribute

elevation: LayerElevation

is3DLayer instance-attribute

is3DLayer: bool

isBasemapLayer instance-attribute

isBasemapLayer: bool

isBroken instance-attribute

isBroken: bool

isFeatureLayer instance-attribute

isFeatureLayer: bool

isGraphicsLayer instance-attribute

isGraphicsLayer: bool

isGroupLayer instance-attribute

isGroupLayer: bool

isNetworkAnalystLayer instance-attribute

isNetworkAnalystLayer: bool

isNetworkDatasetLayer instance-attribute

isNetworkDatasetLayer: bool

isParcelFabricLayer instance-attribute

isParcelFabricLayer: bool

isRasterLayer instance-attribute

isRasterLayer: bool

isSceneLayer instance-attribute

isSceneLayer: bool

isTimeEnabled instance-attribute

isTimeEnabled: bool

isWebLayer instance-attribute

isWebLayer: bool

longName instance-attribute

longName: str

maxThreshold instance-attribute

maxThreshold: float

metadata instance-attribute

metadata: Metadata

minThreshold instance-attribute

minThreshold: float

name instance-attribute

name: str

showLabels instance-attribute

showLabels: bool

symbology instance-attribute

symbology: Symbology

time instance-attribute

time: LayerTime

transparency instance-attribute

transparency: int

visible instance-attribute

visible: bool

Style

Style(name: str, parent: Project)

Bases: Element[None, None, Project]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
__str__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

filter_by

Apply a filter to the StyleItems in the Sytle group.

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
by_class

Group the StyleItems by styleClass.

TYPE: dict[StyleClass, ElementList[StyleItem]]

by_key

Create a mapping of each item to its unique key.

TYPE: dict[str, StyleItem]

by_name

Group the StyleItems by name.

TYPE: dict[str, ElementList[StyleItem]]

by_tags

Group the StyleItems by tag. (will create duplicate item refrences)

TYPE: dict[str, ElementList[StyleItem]]

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

fullname

items

ElementList of StyleItems associated with the Style/stylx.

TYPE: ElementList[StyleItem]

mp_type

mp_type_name

name

TYPE: str

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

unique_name

uri

Source code in src/arcpie/project/elements.py
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
def __init__(self, name: str, parent: Project):
    if name.endswith('.stylx'):
        self.fullname = name
        self._name = name.rsplit('\\', maxsplit=1)[-1].removesuffix('.stylx')
    else:
        self.fullname = self._name = name
    super().__init__(None, parent)
    self.parent = parent
    self.elem = None
    self.cache = dict[str, list[StyleItem]]()

by_class property

by_class: dict[StyleClass, ElementList[StyleItem]]

Group the StyleItems by styleClass.

by_key property

by_key: dict[str, StyleItem]

Create a mapping of each item to its unique key.

by_name property

Group the StyleItems by name.

by_tags property

Group the StyleItems by tag. (will create duplicate item refrences)

cache instance-attribute

cache = dict[str, list[StyleItem]]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = None

fullname instance-attribute

fullname = name

items property

ElementList of StyleItems associated with the Style/stylx.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name: str

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__()
Source code in src/arcpie/project/elements.py
4623
4624
def __repr__(self):
    return f'{type(self).__name__}({self.fullname})'

__str__

__str__() -> str
Source code in src/arcpie/project/elements.py
4626
4627
def __str__(self) -> str:
    return self.fullname

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

filter_by

filter_by(
    key: str | None = None,
    name: str | None = None,
    style_class: StyleClass | None = None,
    tags: Iterable[str] | str | None = None,
) -> ElementList[StyleItem]

Apply a filter to the StyleItems in the Sytle group.

PARAMETER DESCRIPTION

key

A unique key to filter by.

TYPE: str | None DEFAULT: None

name

A name expression to filter by.

TYPE: str | None DEFAULT: None

style_class

A style class to filter by.

TYPE: StyleClass | None DEFAULT: None

tags

Tags to filter by.

TYPE: Iterable[str] | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
def filter_by(
    self,
    key: str | None = None,
    name: str | None = None,
    style_class: mpt.StyleClass | None = None,
    tags: Iterable[str] | str | None = None,
) -> ElementList[StyleItem]:
    """Apply a filter to the StyleItems in the Sytle group.

    Args:
        key: A unique key to filter by.
        name: A name expression to filter by.
        style_class: A style class to filter by.
        tags: Tags to filter by.
    """
    if isinstance(tags, str):
        tags = tags.split(';')
    tags = set(tags) if tags else set()
    items = ElementList[StyleItem]()
    for item in self.items:
        if key and item.elem.key == key:
            items.append(item)
        elif name and item.elem.name == name:
            items.append(item)
        elif tags and tags.issubset(item.tags):
            items.append(item)
        elif style_class and item.elem.styleClass == style_class:
            items.append(item)
    return items

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

StyleItem

StyleItem(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[StyleItem, None, Style]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

category

The category of the StyleItem.

TYPE: str

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

key

The uniqiue key for the StyleItem.

TYPE: str

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

style

The string representation of the parent Style. (use parent for Style object)

style_class

The style class of the StyleItem.

TYPE: StyleClass

tags

A set of tags applied to the StyleItem.

TYPE: set[str]

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

category property

category: str

The category of the StyleItem.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

key property

key: str

The uniqiue key for the StyleItem.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

style property

style

The string representation of the parent Style. (use parent for Style object)

style_class property

style_class: StyleClass

The style class of the StyleItem.

tags property

tags: set[str]

A set of tags applied to the StyleItem.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
4707
4708
4709
4710
4711
4712
4713
4714
4715
def __repr__(self) -> str:
    return (
        f'{type(self).__name__}('
            f'name={self.elem.name}, '
            f'key={self.elem.key}, '
            f'styleClass={self.elem.styleClass}, '
            f'tags={self.tags}'
        ')'
    )

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

Table

Table(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: Element[Table, CIMFeatureTable, Map | GroupLayer]

METHOD DESCRIPTION
__bool__
__eq__
__getattr__
__hash__
__iter__
__len__
__repr__
clear_selection

Clear the current selection.

clone_properties

Copy specified properties from another Table.

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

export_csv

Export the table data to a CSV file.

export_lyrx

Export the lyrx definition of the Table to a file.

get_cim

Get the CIM definition for the Element.

open_view

Open the Table view in the avtive project.

query_as

Context manager for setting the active DefinitionQuery string.

refresh

Refresh all cached elements.

select

Set a selection on the Table using a predicate or query and a method.

select_all

Select all rows in the Table.

selected

Get a list of all selected table records.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

unselected

Get a list of all unselected table records.

update_connection

Update the connection properties of the Table.

ATTRIBUTE DESCRIPTION
cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

chart_map

Chart objects for the Table grouped by chart type. (all types included, empty list for types that don't exist)

TYPE: ChartMap

charts

All Chart objects associated with the Table.

TYPE: tuple[Chart, ...]

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

CIM definition of the Table in dictionary format.

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

data_source

The Table data source as a string.

TYPE: str

definition_queries

A list of all DefinitionQueries for the Table.

TYPE: list[DefinitionQuery]

definition_query

The SQL string of the active DefinitionQuery.

elem

lyrx

A dictionary representation of the table lyrx file.

TYPE: dict[str, Any]

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

selection

Get the selected record OID set.

TYPE: set[int]

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

source_data

Alias for table that allows using Tables and Layers in duck typed loops.

TYPE: Table

table

The base Table object for the Table Layer.

TYPE: Table

unique_name

uri

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

chart_map property

chart_map: ChartMap

Chart objects for the Table grouped by chart type. (all types included, empty list for types that don't exist)

charts property

charts: tuple[Chart, ...]

All Chart objects associated with the Table.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

CIM definition of the Table in dictionary format.

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

data_source property writable

data_source: str

The Table data source as a string.

definition_queries property writable

definition_queries: list[DefinitionQuery]

A list of all DefinitionQueries for the Table.

definition_query property writable

definition_query

The SQL string of the active DefinitionQuery.

elem instance-attribute

elem = cast(MPElem, elem)

lyrx property

lyrx: dict[str, Any]

A dictionary representation of the table lyrx file.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

selection property writable

selection: set[int]

Get the selected record OID set.

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

source_data property

source_data: Table

Alias for table that allows using Tables and Layers in duck typed loops.

table property

table: Table

The base Table object for the Table Layer.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

__bool__

__bool__() -> Literal[True]
Source code in src/arcpie/project/elements.py
3197
3198
3199
def __bool__(self) -> Literal[True]:
    # Override __bool__ to prevent __iter__/__len__ from making a table falsey
    return True

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__iter__

__iter__() -> Iterator[dict[str, Any]]
Source code in src/arcpie/project/elements.py
3194
3195
def __iter__(self) -> Iterator[dict[str, Any]]:
    return iter(self.table)

__len__

__len__() -> int
Source code in src/arcpie/project/elements.py
3191
3192
def __len__(self) -> int:
    return len(self.table)

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

clear_selection

clear_selection() -> Self

Clear the current selection.

Source code in src/arcpie/project/elements.py
3265
3266
3267
def clear_selection(self) -> Self:
    """Clear the current selection."""
    return self.select(method='CLEAR')

clone_properties

clone_properties(
    from_table: TableLike,
    properties: TablePasteProperties = 'ALL',
)

Copy specified properties from another Table.

Source code in src/arcpie/project/elements.py
3319
3320
3321
3322
def clone_properties(self, from_table: TableLike, properties: mpt.TablePasteProperties = 'ALL'):
    """Copy specified properties from another Table."""
    from_table = from_table.elem if isinstance(from_table, Element) else from_table
    self.elem.pasteProperties(from_table, properties)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

export_csv

export_csv(
    outdir: Path | str,
    *,
    name: str | None = None,
    fields: Iterable[str] | None = None,
    sep: str = ',',
    newline: str = '\n',
) -> Path

Export the table data to a CSV file.

PARAMETER DESCRIPTION

outdir

The output directory or file.

TYPE: Path | str

name

An optional name to give the file (default: table.name)

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
def export_csv(self, outdir: Path | str,
               *,
               name: str | None = None,
               fields: Iterable[str] | None = None,
               sep: str = ',',
               newline: str = '\n',
    ) -> Path:
    """Export the table data to a CSV file.

    Args:
        outdir: The output directory or file.
        name: An optional name to give the file (default: `table.name`)
    """
    outdir = Path(outdir)
    outfile = outdir.with_name(name or self.name).with_suffix('.csv')
    outfile.parent.mkdir(exist_ok=True, parents=True)
    table = self.table
    fields = fields or table.fields
    with outfile.open('wt', encoding='utf-8', newline=newline) as fl:
        fl.write(sep.join(fields))
        fl.write(newline)
        fl.writelines(sep.join(line) for line in table[tuple(fields)])
    return outfile

export_lyrx

export_lyrx(
    outdir: Path | str,
    *,
    name: str | None = None,
    indent: int | None = None,
) -> Path

Export the lyrx definition of the Table to a file.

PARAMETER DESCRIPTION

outdir

The directory to place the lyrx file in.

TYPE: Path | str

name

An optional name for the lyrx file. (default is layer.short_name)

TYPE: str | None DEFAULT: None

indent

Indentation level in the output lyrx file. (default: None)

TYPE: int | None DEFAULT: None

relative

Convert all absolute paths in the lyrx file to relative paths. (default: True)

Source code in src/arcpie/project/elements.py
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
def export_lyrx(self,
                outdir: Path | str,
                *,
                name: str | None = None,
                indent: int | None = None,
    ) -> Path:
    """Export the `lyrx` definition of the Table to a file.

    Args:
        outdir: The directory to place the `lyrx` file in.
        name: An optional name for the `lyrx` file. (default is `layer.short_name`)
        indent: Indentation level in the output `lyrx` file. (default: `None`)
        relative: Convert all absolute paths in the `lyrx` file to relative paths. (default: `True`)
    """
    outdir = Path(outdir)
    name = name or self.short_name
    outdir = outdir / name
    outdir = outdir.with_suffix('.lyrx')
    outdir.parent.mkdir(exist_ok=True, parents=True)
    outdir.write_text(json.dumps(self.lyrx, indent=indent))
    return outdir

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

open_view

open_view(show_selected: bool = False) -> None

Open the Table view in the avtive project.

Source code in src/arcpie/project/elements.py
3324
3325
3326
def open_view(self, show_selected: bool = False) -> None:
    """Open the Table view in the avtive project."""
    self.elem.openTableView(show_selected)

query_as

query_as(query: str | None)

Context manager for setting the active DefinitionQuery string.

Example:

>>> print(len(table))
250
>>> with lay.query_as("STATUS = 'ACTIVE'"):
...     print(len(table))
125

Source code in src/arcpie/project/elements.py
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
@contextmanager
def query_as(self, query: str | None):
    """Context manager for setting the active DefinitionQuery string.

    Example:
    ```python
    >>> print(len(table))
    250
    >>> with lay.query_as("STATUS = 'ACTIVE'"):
    ...     print(len(table))
    125
    ```
    """
    cur = self.definition_query
    try:
        self.definition_query = query
        yield self
    finally:
        self.definition_query = cur

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

select

select(
    *,
    predicate: Callable[[dict[str, Any]], bool]
    | None = None,
    query: str | None = None,
    method: Method | Literal['CLEAR', 'ALL'] = 'NEW',
) -> Self

Set a selection on the Table using a predicate or query and a method.

PARAMETER DESCRIPTION

predicate

A function that takes a feature record and returns True if it is to be selected.

TYPE: Callable[[dict[str, Any]], bool] | None DEFAULT: None

query

A SQL where clause that will define the selection (ignored if predicate is set).

TYPE: str | None DEFAULT: None

method

How to resolve the selection with existing selection.

TYPE: Method | Literal['CLEAR', 'ALL'] DEFAULT: 'NEW'

Note

Calling with method = 'CLEAR' and no other arguments will clear the selection. Calling with method = 'ALL' and no other arguments will select all features.

Source code in src/arcpie/project/elements.py
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
def select(self,
           *,
           predicate: Callable[[dict[str, Any]], bool] | None = None,
           query: str | None = None,
           method: mpt.Method | Literal['CLEAR', 'ALL'] = 'NEW',
) -> Self:
    """Set a selection on the Table using a predicate or query and a method.

    Args:
        predicate: A function that takes a feature record and returns True if it is to be selected.
        query: A SQL where clause that will define the selection (ignored if `predicate` is set).
        method: How to resolve the selection with existing selection.

    Note:
        Calling with `method = 'CLEAR'` and no other arguments will clear the selection.
        Calling with `method = 'ALL'` and no other arguments will select all features.
    """
    if method == 'CLEAR':
        self.elem.setSelectionSet([])
        return self

    if method == 'ALL':
        self.elem.setSelectionSet(list(self.table['OID@']))
        return self

    if predicate is not None:
        self.elem.setSelectionSet(
            [r['OID@'] for r in self.table if predicate(r)],
            method
        )
        return self

    with self.query_as(query or self.definition_query):
        self.elem.setSelectionSet(list(self.table['OID@']), method)
    return self

select_all

select_all() -> Self

Select all rows in the Table.

Source code in src/arcpie/project/elements.py
3269
3270
3271
def select_all(self) -> Self:
    """Select all rows in the Table."""
    return self.select(method='ALL')

selected

selected() -> list[dict[str, Any]]

Get a list of all selected table records.

Source code in src/arcpie/project/elements.py
3255
3256
3257
3258
def selected(self) -> list[dict[str, Any]]:
    """Get a list of all selected table records."""
    oids = self.elem.getSelectionSet() or set()
    return [row for row in self.table if row['OID@'] in oids]

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

unselected

unselected() -> list[dict[str, Any]]

Get a list of all unselected table records.

Source code in src/arcpie/project/elements.py
3260
3261
3262
3263
def unselected(self) -> list[dict[str, Any]]:
    """Get a list of all unselected table records."""
    oids = self.elem.getSelectionSet() or set()
    return [row for row in self.table if row['OID@'] not in oids]

update_connection

update_connection(
    new: str,
    current: str | None = None,
    auto_update: bool = True,
    validate: bool = True,
    ignore_case: bool = False,
) -> None

Update the connection properties of the Table.

PARAMETER DESCRIPTION

new

The new connection string.

TYPE: str

current

The connection string to replace.

TYPE: str | None DEFAULT: None

auto_update

Update joins and relates.

TYPE: bool DEFAULT: True

validate

Validate the Table connection before setting it.

TYPE: bool DEFAULT: True

ignore_case

Ignore case in queries.

TYPE: bool DEFAULT: False

Source code in src/arcpie/project/elements.py
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
def update_connection(self,
                      new: str,
                      current: str | None = None,
                      auto_update: bool = True,
                      validate: bool = True,
                      ignore_case: bool = False,
    ) -> None:
    """Update the connection properties of the Table.

    Args:
        new: The new connection string.
        current: The connection string to replace.
        auto_update: Update joins and relates.
        validate: Validate the Table connection before setting it.
        ignore_case: Ignore case in queries.
    """
    self.elem.updateConnectionProperties(current, new, auto_update, validate, ignore_case)

TableFrameElement

TableFrameElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[TableFrameElement, CIMTableFrame]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
apply_style

Apply a TABLE_FRAME StyleItem to the TableFrameElement.

delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

apply_style

apply_style(style_item: StyleItemLike) -> None

Apply a TABLE_FRAME StyleItem to the TableFrameElement.

Source code in src/arcpie/project/elements.py
4967
4968
4969
4970
4971
4972
def apply_style(self, style_item: StyleItemLike) -> None:
    """Apply a `TABLE_FRAME` StyleItem to the TableFrameElement."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass != 'TABLE_FRAME':
        raise ValueError(f'{self} can only use `TABLE_FRAME` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

TextElement

TextElement(
    elem: MPElem | Element[MPElem, CIMDef, Parent],
    parent: Parent | None = None,
)

Bases: LayoutElement[TextElement, CIMGraphicElement]

METHOD DESCRIPTION
__eq__
__getattr__
__hash__
__repr__
apply_style

Apply a TEXT StyleItem to the TextElement.

clone

Make a copy of the TextElement in the parent Layout.

delete

Delete the LayuoutElement from its parent Layout (if it has one).

diff

Generate a diff of two Layers using cim_dict and difflib.unified_diff

get_cim

Get the CIM definition for the Element.

move

Shift the element by the provided x/y deltas.

refresh

Refresh all cached elements.

set_cim

Set a CIM definition for an Element set _force to False to allow CIMType change.

set_text

Set properties of the TextElement text.

ATTRIBUTE DESCRIPTION
anchor

LayoutElement anchor point.

TYPE: Anchor

cache

cache_enabled

See if cache is enabled for this Element

TYPE: bool

children

cim

The CIM object for the Element

TYPE: CIMDef

cim_dict

The Element CIM definition as a Python dictionary

TYPE: dict[str, Any]

cim_type

The CIM object returned by getDefinition('V3')

TYPE: type[CIMDef]

cim_type_name

The name of the CIM object returned by getDefinition('V3')

TYPE: str

elem

font_family

The Font Family of the Text Element. (must be proper name of an installed font)

TYPE: str

font_style

The style name of the font (bold, italic, regular, etc.). Must be supported by font family.

TYPE: str

group

GroupElement the LayoutElement belongs to if it exists.

TYPE: GroupElement | None

height

LayoutElement height in page units.

TYPE: float

locked

The editability state of the LayoutElement.

TYPE: bool

mp_type

mp_type_name

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

overflowing

Determine if the TextElement text is overflowing the TextElement boundary.

TYPE: bool

parent

rotation

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

TYPE: float

short_name

A short name for the Element (elem.name) if one exists otherwise the same as self.name

TYPE: str

text

The text string of the TextElement.

TYPE: str

text_angle

The rotation angle of the text in degrees.

TYPE: float

text_size

The text size in points.

TYPE: float

type

The string name for the LayoutElement type.

TYPE: ElementType

unique_name

uri

visible

Visibility state for the LayoutElement.

TYPE: bool

width

LayoutElement width in page units.

TYPE: float

x

The X position of the LayoutElement anchor point in page units.

TYPE: float

y

The Y position of the LayoutElement anchor point in page units.

TYPE: float

Source code in src/arcpie/project/elements.py
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def __init__(self, elem: MPElem | Element[MPElem, CIMDef, Parent], parent: Parent | None = None) -> None:
    if isinstance(elem, type(self)):
        elem = elem.elem
    self.elem = cast(MPElem, elem)
    self._elemattrs = set(dir(elem))
    self.parent = parent
    self.children = ElementList[Element[Any, Any, Any]]()
    self.mp_type = type(elem)
    self.mp_type_name = self.mp_type.__name__
    self.uri = _get_uri(self.elem)
    self.unique_name = f'{self.name}:{self.uri}'
    self.cache = dict[str, Any]()
    self._cache_enabled = True
    if parent is not None:
        parent.children.append(self)
        self._cache_enabled = parent.cache_enabled

anchor property writable

anchor: Anchor

LayoutElement anchor point.

cache instance-attribute

cache = dict[str, Any]()

cache_enabled property writable

cache_enabled: bool

See if cache is enabled for this Element

Element cache sits on top of the base ArcGIS cache and will not be invalidated unless this flag is set to False or an explicit refresh call is made

When accessing element items in a tight loop, this cache can be ~100x faster (~1us vs ~100us) at the cost of de-sync if lots of changes are being made to the element children/state (e.g. adding maps/layers in a loop).

Methods implemented on the arcpie wrappers will manage this cache, but changes that happen outside arcpie will require manual cache invaludation.

children instance-attribute

children = ElementList[Element[Any, Any, Any]]()

cim property writable

cim: CIMDef

The CIM object for the Element

cim_dict property

cim_dict: dict[str, Any]

The Element CIM definition as a Python dictionary

cim_type property

cim_type: type[CIMDef]

The CIM object returned by getDefinition('V3')

cim_type_name property

cim_type_name: str

The name of the CIM object returned by getDefinition('V3')

elem instance-attribute

elem = cast(MPElem, elem)

font_family property writable

font_family: str

The Font Family of the Text Element. (must be proper name of an installed font)

font_style property writable

font_style: str

The style name of the font (bold, italic, regular, etc.). Must be supported by font family.

group property

group: GroupElement | None

GroupElement the LayoutElement belongs to if it exists.

height property writable

height: float

LayoutElement height in page units.

locked property writable

locked: bool

The editability state of the LayoutElement.

mp_type instance-attribute

mp_type = type(elem)

mp_type_name instance-attribute

mp_type_name = self.mp_type.__name__

name property writable

name

The name of the element (either longName, name, URI, or ID:{hex(id(elem)})

overflowing property

overflowing: bool

Determine if the TextElement text is overflowing the TextElement boundary.

parent instance-attribute

parent = parent

rotation property writable

rotation: float

LayoutElement rotation in degrees. (TableFrameElements cannot be rotated)

short_name property

short_name: str

A short name for the Element (elem.name) if one exists otherwise the same as self.name

text property writable

text: str

The text string of the TextElement.

text_angle property writable

text_angle: float

The rotation angle of the text in degrees.

text_size property writable

text_size: float

The text size in points.

type property

type: ElementType

The string name for the LayoutElement type.

unique_name instance-attribute

unique_name = f'{self.name}:{self.uri}'

uri instance-attribute

uri = _get_uri(self.elem)

visible property writable

visible: bool

Visibility state for the LayoutElement.

width property writable

width: float

LayoutElement width in page units.

x property writable

x: float

The X position of the LayoutElement anchor point in page units.

y property writable

y: float

The Y position of the LayoutElement anchor point in page units.

__eq__

__eq__(other: Any) -> bool
Source code in src/arcpie/project/elements.py
499
500
def __eq__(self, other: Any) -> bool:
    return (isinstance(other, type(self)) and self.elem == other.elem) or super().__eq__(other)

__getattr__

__getattr__(name: str)
Source code in src/arcpie/project/elements.py
488
489
490
491
492
493
494
def __getattr__(self, name: str):
    try:
        return super().__getattribute__(name)
    except AttributeError:
        if name in self._elemattrs:
            return getattr(self.elem, name)
        raise

__hash__

__hash__() -> int
Source code in src/arcpie/project/elements.py
502
503
def __hash__(self) -> int:
    return hash(self.elem._arc_object)  # type: ignore

__repr__

__repr__() -> str
Source code in src/arcpie/project/elements.py
496
497
def __repr__(self) -> str:
    return f'{type(self).__name__}({self.short_name if not self.name.startswith('ID:0x') else self.parent})'

apply_style

apply_style(style_item: StyleItemLike) -> None

Apply a TEXT StyleItem to the TextElement.

Source code in src/arcpie/project/elements.py
5442
5443
5444
5445
5446
5447
def apply_style(self, style_item: StyleItemLike) -> None:
    """Apply a `TEXT` StyleItem to the TextElement."""
    style_item = style_item.elem if isinstance(style_item, Element) else style_item
    if style_item.styleClass != 'TEXT':
        raise ValueError(f'{self} can only use `TEXT` style classes, not {style_item.styleClass}')
    self.elem.applyStyleItem(style_item)

clone

clone(name: str | None = None) -> Self

Make a copy of the TextElement in the parent Layout.

PARAMETER DESCRIPTION

name

An optional name to give the copy. (default: {name}{n+1})

TYPE: str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
5449
5450
5451
5452
5453
5454
5455
5456
5457
def clone(self, name: str | None = None) -> Self:
    """Make a copy of the TextElement in the parent Layout.

    Args:
        name: An optional name to give the copy. (default: `{name}{n+1}`)
    """
    new = type(self)(self.elem.clone(), self.parent)
    new.name = name or new.name
    return new

delete

delete() -> None

Delete the LayuoutElement from its parent Layout (if it has one).

Source code in src/arcpie/project/elements.py
4944
4945
4946
4947
def delete(self) -> None:
    """Delete the LayuoutElement from its parent Layout (if it has one)."""
    if self.parent is not None:
        self.parent.delete_element(self)

diff classmethod

diff(
    a: Self, b: Self, *, outfile: Path | str | None = None
) -> str

Generate a diff of two Layers using cim_dict and difflib.unified_diff

PARAMETER DESCRIPTION

a

The A Element of the diff

TYPE: Self

b

The B Element of the diff

TYPE: Self

outfile

An optional .diff file to write the diff to

TYPE: Path | str | None DEFAULT: None

Source code in src/arcpie/project/elements.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
@classmethod
def diff(cls, a: Self, b: Self, *, outfile: Path | str | None = None) -> str:
    """Generate a diff of two Layers using `cim_dict` and `difflib.unified_diff`

    Args:
        a: The A Element of the diff
        b: The B Element of the diff
        outfile: An optional `.diff` file to write the diff to
    """
    diff = '\n'.join(difflib.unified_diff(
        json.dumps(a.cim_dict, indent=2).split('\n'),
        json.dumps(b.cim_dict, indent=2).split('\n'),
        fromfile=f'{a.name} (a)',
        tofile=f'{b.name} (b)',
    ))
    if outfile:
        Path(outfile).with_suffix('.diff').write_text(diff)
    return diff

get_cim

get_cim() -> CIMDef

Get the CIM definition for the Element.

Source code in src/arcpie/project/elements.py
447
448
449
450
451
452
453
454
455
456
457
458
459
def get_cim(self) -> CIMDef:
    """Get the CIM definition for the Element."""
    elem = self.elem
    # Try really hard to access the CIM definition,
    # even if it's not available in the public API
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if getter := getattr(arc_object, 'GetCimJSONString', None):
                with suppress(Exception):
                    assert callable(getter)
                    return jsontocim.GetJSONTypeOBJ(json.loads(cast(str, getter())))  # type: ignore
        raise AttributeError(f'{type(self).__name__} has no implemented CIM getter')
    return cast(CIMDef, elem.getDefinition('V3'))

move

move(x: float = 0.0, y: float = 0.0) -> None

Shift the element by the provided x/y deltas.

Source code in src/arcpie/project/elements.py
4949
4950
4951
4952
def move(self, x: float = 0.0, y: float = 0.0) -> None:
    """Shift the element by the provided x/y deltas."""
    self.x += x
    self.y += y

refresh

refresh(*props: str) -> None

Refresh all cached elements.

By default, all list* function results are cached and copies of those objects are returned. If a state change occurs (elements are added/removed) refresh() should be called.

PARAMETER DESCRIPTION

props

varargs for all cache entries to clear (default: <all>)

TYPE: str DEFAULT: ()

Source code in src/arcpie/project/elements.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
def refresh(self, *props: str) -> None:
    """Refresh all cached elements.

    By default, all list* function results are cached and copies of those
    objects are returned. If a state change occurs (elements are added/removed)
    refresh() should be called.

    Args:
        props: varargs for all cache entries to clear (default: `<all>`)
    """
    if not props:
        self.cache.clear()
        self.children.clear()
        return
    orphans = set[Element[Any, Any, Self]]()
    for prop in props:
        for orphan in self.cache.pop(prop, []):
            orphans.add(orphan)
    self.children = ElementList(
        chld for chld in self.children
        if chld not in orphans
    )

set_cim

set_cim(cim: CIMDef, _force: bool = False) -> None

Set a CIM definition for an Element set _force to False to allow CIMType change. (must be valid instance of original CIM by default)

Source code in src/arcpie/project/elements.py
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def set_cim(self, cim: CIMDef, _force: bool = False) -> None:
    """Set a CIM definition for an Element set `_force` to False to allow CIMType change.
    (must be valid instance of original CIM by default)"""
    elem = self.elem
    target_type = type(self.cim)
    if not isinstance(cim, target_type) and not _force:
        raise ValueError(f'{self} expects cim type {target_type.__name__}, got {type(cim).__name__}')
    if _cimless(elem):
        if arc_object := getattr(elem, '_arc_object', None):
            if setter := getattr(arc_object, 'SetCimJSONString', None):
                with suppress(Exception):
                    assert callable(setter)
                    setter(cim)
        raise AttributeError(f'{type(self).__name__} has no implemented CIM setter')
    elem.setDefinition(cast(str, cim))  # Signature here is wrong CIM obj OR string name

set_text

set_text(
    *,
    text: str | None = None,
    family: str | None = None,
    style: str | None = None,
    size: float | None = None,
    angle: float | None = None,
) -> None

Set properties of the TextElement text.

PARAMETER DESCRIPTION

text

Set the text.

TYPE: str | None DEFAULT: None

family

Set the font family.

TYPE: str | None DEFAULT: None

style

Set the font style.

TYPE: str | None DEFAULT: None

size

Set the font size.

TYPE: float | None DEFAULT: None

angle

Set the font angle.

TYPE: float | None DEFAULT: None

Source code in src/arcpie/project/elements.py
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
def set_text(self,
             *,
             text: str | None = None,
             family: str | None = None,
             style: str | None = None,
             size: float | None = None,
             angle: float | None = None,
) -> None:
    """Set properties of the TextElement text.

    Args:
        text: Set the text.
        family: Set the font family.
        style: Set the font style.
        size: Set the font size.
        angle: Set the font angle.
    """
    if text is not None:
        self.text = text
    if family is not None:
        self.font_family = family
    if style is not None:
        self.font_style = style
    if size is not None:
        self.size = size
    if angle is not None:
        self.angle = angle

ToolboxConf

Bases: TypedDict

ATTRIBUTE DESCRIPTION
isDefaultToolbox

TYPE: bool

toolboxPath

TYPE: Path | str

isDefaultToolbox instance-attribute

isDefaultToolbox: bool

toolboxPath instance-attribute

toolboxPath: Path | str

UnwrapError

UnwrapError(expects: int, have: int)

Bases: KeyError, ValueError, IndexError

ATTRIBUTE DESCRIPTION
expects

have

Source code in src/arcpie/project/elements.py
527
528
529
530
def __init__(self, expects: int, have: int):
    super().__init__(f'expected {expects}: have {have}')
    self.expects = expects
    self.have = have

expects instance-attribute

expects = expects

have instance-attribute

have = have