Skip to content

Project

CLASS DESCRIPTION
BookmarkMapSeries

Wrapper around an arcpy.mp BookmarkMapSeries object that provides an ergonomic interface

Layer

mp.Layer wrapper

Layout
Manager

Base access interfaces for all manager classes. Specific interfaces are defined in the subclass

Map
MapSeries

Wrapper around an arcpy.mp MapSeries object that provides an ergonomic interface

MappingWrapper

Internal wrapper class for wrapping existing objects with new functionality

Project

Wrapper for an ArcGISProject (.aprx)

Table
Wildcard

Clarify that the string passed to a Manager index is a wildcard so the type checker knows you're getting a Sequence back

FUNCTION DESCRIPTION
name_of

Handle the naming hierarchy of mapping objects URI -> longName -> name

safe_name

Remove invalid filename characters from a string

BookmarkMapSeries

BookmarkMapSeries(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[BookmarkMapSeries, CIMBookmarkMapSeries], BookmarkMapSeries

Wrapper around an arcpy.mp BookmarkMapSeries object that provides an ergonomic interface

METHOD DESCRIPTION
__getitem__

Allow indexing the BookmarkMapSeries by name, index, or Bookmark object

ATTRIBUTE DESCRIPTION
parent

The parent object for the wrapper

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

__getitem__

__getitem__(
    page: int | str | Bookmark,
) -> BookmarkMapSeries

Allow indexing the BookmarkMapSeries by name, index, or Bookmark object

Source code in src/arcpie/project.py
251
252
253
254
255
256
257
258
259
260
def __getitem__(self, page: int|str|_Bookmark) -> BookmarkMapSeries:
    """Allow indexing the BookmarkMapSeries by name, index, or Bookmark object"""
    match page:
        case _Bookmark():
            self.currentBookmark = page
        case str():
            self.currentPageNumber = self.getPageNumberFromName(page)
        case int():
            self.currentPageNumber = page
    return self

Layer

Layer(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[Layer, CIMBaseLayer], Layer

mp.Layer wrapper

METHOD DESCRIPTION
export_lyrx

Export the layer to a lyrx file in the target directory

import_lyrx

Import the layer state from an lyrx file

ATTRIBUTE DESCRIPTION
feature_class

Get a arcpie.FeatureClass object that is initialized using the layer and its current state

TYPE: FeatureClass

lyrx

Get a dictionary representation of the layer that can be saved to an lyrx file using json.dumps

TYPE: dict[str, Any] | None

parent

The parent object for the wrapper

symbology

Get the base symbology object for the layer

TYPE: Symbology

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

feature_class property

feature_class: FeatureClass

Get a arcpie.FeatureClass object that is initialized using the layer and its current state

lyrx property

lyrx: dict[str, Any] | None

Get a dictionary representation of the layer that can be saved to an lyrx file using json.dumps

Note

GroupLayer objects will return a lyrx template with all sub-layers included

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

symbology property

symbology: Symbology

Get the base symbology object for the layer

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

export_lyrx

export_lyrx(out_dir: Path | str) -> None

Export the layer to a lyrx file in the target directory

PARAMETER DESCRIPTION

out_dir

The location to export the mapx to

TYPE: Path | str

Source code in src/arcpie/project.py
200
201
202
203
204
205
206
207
208
209
210
def export_lyrx(self, out_dir: Path|str) -> None:
    """Export the layer to a lyrx file in the target directory

    Args:
        out_dir (Path|str): The location to export the mapx to
    """
    out_dir = Path(out_dir)
    target = out_dir / f'{safe_name(self.longName)}.lyrx'
    # Make Containing directory for grouped layers
    target.parent.mkdir(exist_ok=True, parents=True)
    target.write_text(json.dumps(self.lyrx, indent=2), encoding='utf-8')

import_lyrx

import_lyrx(lyrx: Path | str) -> None

Import the layer state from an lyrx file

PARAMETER DESCRIPTION

lyrx

The lyrx file to update this layer with

TYPE: Path | str

Note

CIM changes require the APRX to be saved to take effect. If you are accessing this layer via a Project, use project.save() after importing the layerfile

Source code in src/arcpie/project.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def import_lyrx(self, lyrx: Path|str) -> None:
    """Import the layer state from an lyrx file

    Args:
        lyrx (Path|str): The lyrx file to update this layer with

    Note:
        CIM changes require the APRX to be saved to take effect. If you are accessing this
        layer via a Project, use `project.save()` after importing the layerfile
    """
    _lyrx = LayerFile(str(lyrx))
    _lyrx_layers = {l.name: l for l in _lyrx.listLayers() if hasattr(l, 'name')}
    if not (_lyrx_layer := _lyrx_layers.get(self.name)):
        print(f'{self.name} not found in {str(lyrx)}')
    else:
        # Update Connection
        _lyrx_layer = Layer(_lyrx_layer)
        _lyrx_cim_dict = _lyrx_layer.cim_dict or {}
        _lyrx_layer_cim = _lyrx_layer.cim
        if _lyrx_cim_dict.get('featureTable') and _lyrx_cim_dict['featureTable'].get('dataConnection'):
            _lyrx_layer_cim.featureTable.dataConnection = self.cim.featureTable.dataConnection # type: ignore (this is how CIM works)
        try:
            self.setDefinition(_lyrx_layer_cim) # pyright: ignore[reportArgumentType]
        except AttributeError:
            print(f'Failed to update CIM for {self.__class__.__name__}{self.name}')

Layout

Layout(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[Layout, CIMLayout], Layout

METHOD DESCRIPTION
to_pdf

Get the bytes for a pdf export of the Layout

ATTRIBUTE DESCRIPTION
mapseries

Get the Layout MapSeries/BookmarkMapSeries if it exists

TYPE: MapSeries | BookmarkMapSeries | None

pagx

Access the raw CIM dictionary of the layout

TYPE: dict[str, Any]

parent

The parent object for the wrapper

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

mapseries property

mapseries: MapSeries | BookmarkMapSeries | None

Get the Layout MapSeries/BookmarkMapSeries if it exists

pagx property

pagx: dict[str, Any]

Access the raw CIM dictionary of the layout

RETURNS DESCRIPTION
dict[str, Any]

A dictionary representation of the pagx json

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

to_pdf

to_pdf(**settings: Unpack[PDFSetting]) -> BytesIO

Get the bytes for a pdf export of the Layout

PARAMETER DESCRIPTION

**settings

Optional settings for the export (default: PDFDefault)

TYPE: PDFSetting DEFAULT: {}

RETURNS DESCRIPTION
bytes

Raw bytes of the PDF for use in a write operation or stream

Example
    # Get the layout object from a project file
    lyt = prj.layouts['Layout_1']

    # Create a pdf then write the output of to_pdf to it
    pdf = Path('pdf_path').write_bytes(lyt.to_pdf())
Source code in src/arcpie/project.py
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
def to_pdf(self, **settings: Unpack[PDFSetting]) -> BytesIO:
    """Get the bytes for a pdf export of the Layout

    Args:
        **settings (PDFSetting): Optional settings for the export (default: `PDFDefault`)

    Returns:
        (bytes): Raw bytes of the PDF for use in a write operation or stream

    Example:
        ```python
            # Get the layout object from a project file
            lyt = prj.layouts['Layout_1']

            # Create a pdf then write the output of to_pdf to it
            pdf = Path('pdf_path').write_bytes(lyt.to_pdf())
        ```   
    """
    with NamedTemporaryFile() as tmp:
        _settings = PDFDefault.copy()
        for arg in _settings:
            if val := settings.get(arg):
                _settings[arg] = val
        pdf = self.exportToPDF(tmp.name, **_settings)
        with Path(pdf).open('rb') as fl:
            return BytesIO(fl.read())

Manager

Manager(objs: Iterable[_MappingObject])

Bases: Generic[_MappingObject]

Base access interfaces for all manager classes. Specific interfaces are defined in the subclass

Index itentifiers are URI -> longName -> name depending on what is available in the managed class

METHOD DESCRIPTION
__contains__

Check to see if a URI/name is present in the Manager

__getitem__

Access objects using a regex pattern (re.compile), wildcard (STRING), index, slice, name, or URI

get

Get a value from the Project with a safe default value

ATTRIBUTE DESCRIPTION
names

Get the names of all managed objects (skips URIs)

TYPE: list[str]

objects

Get a list of all managed objects

TYPE: list[_MappingObject]

uris

Get URIs/CIMPATH for all managed objects

TYPE: list[str]

Source code in src/arcpie/project.py
631
632
633
634
635
def __init__(self, objs: Iterable[_MappingObject]) -> None:
    self._objects: dict[str, _MappingObject] = {}
    for o in objs:
        if (_uri := o.uri) not in self._objects:
            self._objects[_uri] = o

names property

names: list[str]

Get the names of all managed objects (skips URIs)

objects property

objects: list[_MappingObject]

Get a list of all managed objects

uris property

uris: list[str]

Get URIs/CIMPATH for all managed objects

Note

Default to a Python id() call if no URI is present

__contains__

__contains__(name: str | _MappingObject) -> bool

Check to see if a URI/name is present in the Manager

Source code in src/arcpie/project.py
705
706
707
708
709
710
711
def __contains__(self, name: str|_MappingObject) -> bool:
    """Check to see if a URI/name is present in the Manager"""
    match name:
        case str():
            return True if self.get(name) else False
        case _:
            return any(o == name for o in self.objects)

__getitem__

__getitem__(name: str) -> _MappingObject
__getitem__(name: int) -> _MappingObject
__getitem__(name: Wildcard) -> list[_MappingObject]
__getitem__(name: Pattern[str]) -> list[_MappingObject]
__getitem__(name: slice) -> list[_MappingObject]
__getitem__(
    name: str | Wildcard | Pattern[str] | int | slice,
) -> _MappingObject | list[_MappingObject]

Access objects using a regex pattern (re.compile), wildcard (STRING), index, slice, name, or URI

Source code in src/arcpie/project.py
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
def __getitem__(self, name: str|Wildcard|re.Pattern[str]|int|slice) -> _MappingObject|list[_MappingObject]:
    """Access objects using a regex pattern (re.compile), wildcard (*STRING*), index, slice, name, or URI"""
    if isinstance(name, str) and '*' in name:
        name = Wildcard(name) # Allow passing a wildcard directly (lose type inference)
    match name:
        case int() | slice():
            return self.objects[name] # Will raise IndexError
        case str(name) if name in self._objects:
            return self._objects[name]
        case str(name) if name in self.names:
            for o in self.objects:
                if o.unique_name == name:
                    return o
        case re.Pattern():
            return [o for o in self.objects if name.match(name_of(o, skip_uri=True))]
        case Wildcard():
            return [o for o in self.objects if all(part in name_of(o, skip_uri=True) for part in name.split('*'))]
        case _ :
            pass # Fallthrough to raise a KeyError

    raise KeyError(f'{name} not found in objects: ({self.names})')

get

get(name: str) -> _MappingObject
get(name: Wildcard) -> list[_MappingObject]
get(
    name: str, default: _Default
) -> _MappingObject | _Default
get(
    name: Wildcard, default: _Default
) -> list[_MappingObject] | _Default
get(
    name: str | Wildcard, default: _Default | None = None
) -> (
    _MappingObject | list[_MappingObject] | _Default | None
)

Get a value from the Project with a safe default value

Source code in src/arcpie/project.py
698
699
700
701
702
703
def get(self, name: str|Wildcard, default: _Default|None=None) -> _MappingObject|list[_MappingObject]|_Default|None:
    """Get a value from the Project with a safe default value"""
    try:
        return self[name]
    except KeyError:
        return default

Map

Map(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[Map, CIMMapDocument], Map

METHOD DESCRIPTION
export_assoc_lyrx

Export all child layers to lyrx files the target directory

export_mapx

Export the map definitions to a mapx file in the target directory

import_assoc_lyrx

Imports lyrx files that were exported using the export_assoc_lyrx method

ATTRIBUTE DESCRIPTION
bookmarks

Get a BookmarkManager for all bookmarks in the Map

TYPE: BookmarkManager

elevation_surfaces

Get an ElevationSurfaceManager for all elevation surfaces in the Map

TYPE: ElevationSurfaceManager

layers

Get a LayerManager for all layers in the Map

TYPE: LayerManager

local_layers

Get a LayerManager for all non-web layers in the Map

TYPE: LayerManager

parent

The parent object for the wrapper

tables

Get a TableManager for all tables in the Map

TYPE: TableManager

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

bookmarks cached property

bookmarks: BookmarkManager

Get a BookmarkManager for all bookmarks in the Map

elevation_surfaces cached property

elevation_surfaces: ElevationSurfaceManager

Get an ElevationSurfaceManager for all elevation surfaces in the Map

layers cached property

layers: LayerManager

Get a LayerManager for all layers in the Map

local_layers cached property

local_layers: LayerManager

Get a LayerManager for all non-web layers in the Map

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

tables cached property

tables: TableManager

Get a TableManager for all tables in the Map

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

export_assoc_lyrx

export_assoc_lyrx(
    out_dir: Path | str,
    *,
    skip_groups: bool = False,
    skip_grouped: bool = False,
) -> None

Export all child layers to lyrx files the target directory

PARAMETER DESCRIPTION

out_dir

The location to export the lyrx files to

TYPE: Path | str

skip_groups

Skip group layerfiles and export each layer individually in a group subdirectory (default: False)

TYPE: bool DEFAULT: False

skip_grouped

Inverse of skip groups and instead only exports the group lyrx, skipping the individual layers (default: False)

TYPE: bool DEFAULT: False

Source code in src/arcpie/project.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
def export_assoc_lyrx(self, out_dir: Path|str, *, skip_groups: bool=False, skip_grouped: bool=False) -> None:
    """Export all child layers to lyrx files the target directory

    Args:
        out_dir (Path|str): The location to export the lyrx files to
        skip_groups (bool): Skip group layerfiles and export each layer individually in a group subdirectory (default: False)
        skip_grouped (bool): Inverse of skip groups and instead only exports the group lyrx, skipping the individual layers (default: False)
    """
    out_dir = Path(out_dir)
    for layer in self.layers:
        if layer.isGroupLayer and skip_groups:
            continue
        try:
            layer.export_lyrx(out_dir / self.unique_name)
        except json.JSONDecodeError as e:
            print(f'Failed to export layer: {layer}: {e}')

    for table in self.tables:
        try:
            table.export_lyrx(out_dir / self.unique_name)
        except json.JSONDecodeError as e:
            print(f'Failed to export table: {table}: {e}')

export_mapx

export_mapx(out_dir: Path | str) -> None

Export the map definitions to a mapx file in the target directory

PARAMETER DESCRIPTION

out_dir

The location to export the mapx to

TYPE: Path | str

Source code in src/arcpie/project.py
428
429
430
431
432
433
434
435
def export_mapx(self, out_dir: Path|str) -> None:
    """Export the map definitions to a mapx file in the target directory

    Args:
        out_dir (Path|str): The location to export the mapx to
    """
    target = Path(out_dir) / f'{self.unique_name}'
    target.write_text(json.dumps(self.mapx, indent=2), encoding='utf-8')

import_assoc_lyrx

import_assoc_lyrx(
    lyrx_dir: Path | str, *, skip_groups: bool = False
) -> None

Imports lyrx files that were exported using the export_assoc_lyrx method

PARAMETER DESCRIPTION

lyrx_dir

The directory containing the previously exported lyrx files

TYPE: Path | str

Note

CIM changes require the APRX to be saved to take effect. If you are accessing this layer via a Project, use project.save() after importing the layerfile

Source code in src/arcpie/project.py
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
def import_assoc_lyrx(self, lyrx_dir: Path|str, *, skip_groups: bool=False) -> None:
    """Imports lyrx files that were exported using the `export_assoc_lyrx` method

    Args:
        lyrx_dir (Path|str): The directory containing the previously exported lyrx files

    Note:
        CIM changes require the APRX to be saved to take effect. If you are accessing this
        layer via a Project, use `project.save()` after importing the layerfile
    """
    lyrx_dir = Path(lyrx_dir)
    for lyrx_path in lyrx_dir.rglob('*.lyrx'):
        _lyrx_name = str(lyrx_path.relative_to(lyrx_dir).with_suffix(''))
        # Since layers can have invalid file names, we need to check the `safe` name as well
        _safe_layers = {safe_name(n): n for n in self.layers.names}
        _safe_tables = {safe_name(n): n for n in self.tables.names}
        if _lyrx_name in self.layers or _lyrx_name in _safe_layers:
            _lyrx_name = _safe_layers.get(_lyrx_name, _lyrx_name)
            if self.layers[_lyrx_name].isGroupLayer and skip_groups:
                continue
            self.layers[_lyrx_name].import_lyrx(lyrx_path)
        elif _lyrx_name in self.tables or _lyrx_name in _safe_tables:
            _lyrx_name = _safe_tables.get(_lyrx_name, _lyrx_name)
            self.tables[_lyrx_name].import_lyrx(lyrx_path)

MapSeries

MapSeries(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[MapSeries, CIMMapSeries], MapSeries

Wrapper around an arcpy.mp MapSeries object that provides an ergonomic interface

METHOD DESCRIPTION
__getitem__

Allow indexing a mapseries by a page name or a page index/number

to_pdf

Export the MapSeries to a PDF, See Layer.to_pdf for more info

ATTRIBUTE DESCRIPTION
current_page_name

Get the name of the active mapseries page

TYPE: str

feature_class

Get the FeatureClass of the parent layer

TYPE: FeatureClass

layer

Get the mapseries target layer

TYPE: Layer

map

Get the map object that is being seriesed

TYPE: Map

pageRow

Get a Row object for the active mapseries page

page_field

Get fieldname used as pagename

TYPE: str

page_field_names

Get all fieldnames for the mapseriesed features

TYPE: list[str]

page_values

Get a mapping of values for the current page

TYPE: dict[str, Any]

parent

The parent object for the wrapper

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

valid_pages

Get all valid page names for the MapSeries

TYPE: list[str]

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

current_page_name property

current_page_name: str

Get the name of the active mapseries page

feature_class property

feature_class: FeatureClass

Get the FeatureClass of the parent layer

layer property

layer: Layer

Get the mapseries target layer

map property

map: Map

Get the map object that is being seriesed

pageRow property

pageRow

Get a Row object for the active mapseries page

page_field property

page_field: str

Get fieldname used as pagename

page_field_names cached property

page_field_names: list[str]

Get all fieldnames for the mapseriesed features

page_values property

page_values: dict[str, Any]

Get a mapping of values for the current page

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

valid_pages property

valid_pages: list[str]

Get all valid page names for the MapSeries

__getitem__

__getitem__(page: int | str) -> MapSeries

Allow indexing a mapseries by a page name or a page index/number

Source code in src/arcpie/project.py
347
348
349
350
351
352
353
354
355
356
357
358
def __getitem__(self, page: int|str) -> MapSeries:
    """Allow indexing a mapseries by a page name or a page index/number"""
    match page:
        case str():
            if page not in self.valid_pages:
                raise KeyError(f"{page} is not a valid page name!")
            self.currentPageNumber = self.getPageNumberFromName(page)
        case int():
            if page not in range(1, self.pageCount):
                raise IndexError(f"{self} only has {self.pageCount} pages, {page} out of range")
            self.currentPageNumber = page
    return self

to_pdf

to_pdf(**settings: Unpack[MapSeriesPDFSetting]) -> BytesIO

Export the MapSeries to a PDF, See Layer.to_pdf for more info

PARAMETER DESCRIPTION

**settings

Passthrough kwargs for layout.exportToPDF

TYPE: Unpack[MapSeriesPDFSetting] DEFAULT: {}

Note

By default, printing a mapseries will print all pages to a single file. To only print the active page:

>>> ms.to_pdf(page_range_type='CURRENT')

Source code in src/arcpie/project.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def to_pdf(self, **settings: Unpack[MapSeriesPDFSetting]) -> BytesIO:
    """Export the MapSeries to a PDF, See Layer.to_pdf for more info

    Args:
        **settings (Unpack[MapSeriesPDFSetting]): Passthrough kwargs for layout.exportToPDF

    Note:
        By default, printing a mapseries will print all pages to a single file. To only print
        the active page:
        ```python
        >>> ms.to_pdf(page_range_type='CURRENT')
        ```
    """
    _settings = MapseriesPDFDefault.copy()
    _settings.update(settings)
    with NamedTemporaryFile() as tmp:
        _pdf = self.exportToPDF(tmp.name, **_settings)
        with Path(_pdf).open('rb') as fl:
            return BytesIO(fl.read())

MappingWrapper

MappingWrapper(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: Generic[_MapType, _CIMType]

Internal wrapper class for wrapping existing objects with new functionality

Usage
>>> MappingWraper[mp.<type>, cim.<type>](mp.<type>)
Note

All

ATTRIBUTE DESCRIPTION
parent

The parent object for the wrapper

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

Project

Project(
    aprx_path: str | Path | Literal["CURRENT"] = "CURRENT",
)

Wrapper for an ArcGISProject (.aprx)

Usage
>>> prj = Project('<path/to/aprx>')
>>> lay = prj.layouts.get('My Layout')
>>> Path('My Layout.pdf').write_bytes(prj.layouts.get('My Layout').to_pdf())
4593490 # Bytes written
>>> for map in prj.maps:
...     print(f'{map.name} has {len(map.layers)} layers')
My Map has 5 layers
My Map 2 has 15 layers
Other Map has 56 layers
METHOD DESCRIPTION
__getitem__

Resolve the name by looking in Maps, then Layouts, then Reports

export_layers

Export all layers in the project to a structured directory of layerfiles

export_mapx

Export all maps to a directory

export_pagx

Export all layouts to a directory

import_layers

Import a structured directory of layerfiles generated with export_layers

import_mapx

Import a mapx file into this project

import_pagx

Import a pagx file into this project

refresh

Clear cached object managers

save

Save this project

save_as

Saves the project under a new name

ATTRIBUTE DESCRIPTION
aprx

Get the base ArcGISProject for the Project

TYPE: ArcGISProject

broken_layers

Get a LayerManager for all layers in the project with broken datasources

TYPE: LayerManager

broken_tables

Get a TableManager for all tables in the project with broken datasources

TYPE: TableManager

layouts

Get a LayoutManager for the Project layouts

TYPE: LayoutManager

maps

Get a MapManager for the Project maps

TYPE: MapManager

name

Get the file name of the wrapped aprx minus the file extension

TYPE: str

reports

Get a ReportManager for the Project reports

TYPE: ReportManager

Source code in src/arcpie/project.py
744
745
def __init__(self, aprx_path: str|Path|Literal['CURRENT']='CURRENT') -> None:
    self._path = str(aprx_path)

aprx property

aprx: ArcGISProject

Get the base ArcGISProject for the Project

broken_layers property

broken_layers: LayerManager

Get a LayerManager for all layers in the project with broken datasources

broken_tables property

broken_tables: TableManager

Get a TableManager for all tables in the project with broken datasources

layouts cached property

layouts: LayoutManager

Get a LayoutManager for the Project layouts

maps cached property

maps: MapManager

Get a MapManager for the Project maps

name property

name: str

Get the file name of the wrapped aprx minus the file extension

reports cached property

reports: ReportManager

Get a ReportManager for the Project reports

__getitem__

__getitem__(name: str) -> Map | Layout | Report
__getitem__(
    name: Wildcard,
) -> list[Map] | list[Layout] | list[Report]
__getitem__(name: str | Wildcard) -> Any | list[Any]

Resolve the name by looking in Maps, then Layouts, then Reports

Source code in src/arcpie/project.py
815
816
817
818
819
820
def __getitem__(self, name: str | Wildcard) -> Any | list[Any]:
    """Resolve the name by looking in Maps, then Layouts, then Reports"""
    _obj = self.maps.get(name, None) or self.layouts.get(name, None) or self.reports.get(name, None)
    if _obj is None:
        raise KeyError(f'{name} not found in {self.name}')
    return _obj

export_layers

export_layers(
    target_dir: Path | str,
    *,
    skip_groups: bool = False,
    skip_grouped: bool = False,
) -> None

Export all layers in the project to a structured directory of layerfiles

PARAMETER DESCRIPTION

target_dir

The target directory to export the layerfiles to

TYPE: Path | str

skip_groups

Skip group layerfiles and export each layer individually in a group subdirectory (default: False)

TYPE: bool DEFAULT: False

skip_grouped

Inverse of skip groups and instead only exports the group lyrx, skipping the individual layers (default: False)

TYPE: bool DEFAULT: False

Source code in src/arcpie/project.py
914
915
916
917
918
919
920
921
922
923
924
def export_layers(self, target_dir: Path|str, *, skip_groups: bool = False, skip_grouped: bool = False) -> None:
    """Export all layers in the project to a structured directory of layerfiles

    Args:
        target_dir (Path|str): The target directory to export the layerfiles to
        skip_groups (bool): Skip group layerfiles and export each layer individually in a group subdirectory (default: False)
        skip_grouped (bool): Inverse of skip groups and instead only exports the group lyrx, skipping the individual layers (default: False)
    """
    target_dir = Path(target_dir)
    for m in self.maps:
        m.export_assoc_lyrx(target_dir, skip_groups=skip_groups, skip_grouped=skip_grouped)

export_mapx

export_mapx(target_dir: Path | str) -> None

Export all maps to a directory

Source code in src/arcpie/project.py
907
908
909
910
911
912
def export_mapx(self, target_dir: Path|str) -> None:
    """Export all maps to a directory"""
    target_dir = Path(target_dir)
    target_dir.mkdir(exist_ok=True, parents=True)
    for m in self.maps:
        (target_dir / f'{m.unique_name}.mapx').write_text(json.dumps(m.mapx, indent=2), encoding='utf-8')

export_pagx

export_pagx(target_dir: Path | str) -> None

Export all layouts to a directory

Source code in src/arcpie/project.py
878
879
880
881
882
883
def export_pagx(self, target_dir: Path|str) -> None:
    """Export all layouts to a directory"""
    target_dir = Path(target_dir)
    target_dir.mkdir(exist_ok=True, parents=True)
    for layout in self.layouts:
        (target_dir / f'{layout.unique_name}.pagx').write_text(json.dumps(layout.pagx, indent=2), encoding='utf-8')

import_layers

import_layers(src_dir: Path | str) -> None

Import a structured directory of layerfiles generated with export_layers

PARAMETER DESCRIPTION

src_dir

A directory containing layer files in map directories and group directories

TYPE: Path | str

Note

CIM changes require the APRX to be saved to take effect. If you are accessing this layer via a Project, use project.save() after importing the layerfile

Source code in src/arcpie/project.py
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
def import_layers(self, src_dir: Path|str) -> None:
    """Import a structured directory of layerfiles generated with `export_layers`

    Args:
        src_dir (Path|str): A directory containing layer files in map directories and group directories

    Note:
        CIM changes require the APRX to be saved to take effect. If you are accessing this
        layer via a Project, use `project.save()` after importing the layerfile
    """
    src_dir = Path(src_dir)
    for m in self.maps:
        map_dir = src_dir / m.unique_name
        if not map_dir.exists():
            print(f'Map {m.unique_name} does not have a valid source in the source directory, skipping')
            continue
        m.import_assoc_lyrx(map_dir)

    self.refresh('layers')

import_mapx

import_mapx(mapx: Path | str) -> Map

Import a mapx file into this project

PARAMETER DESCRIPTION

mapx

The path to the mapx document

TYPE: Path | str

RETURNS DESCRIPTION
Map

A Map parented to this project

Source code in src/arcpie/project.py
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
def import_mapx(self, mapx: Path|str) -> Map:
    """Import a mapx file into this project

    Args:
        mapx (Path|str): The path to the mapx document

    Returns:
        (Map): A Map parented to this project
    """
    mapx = Path(mapx)
    if not mapx.suffix == '.mapx':
        raise ValueError(f'{mapx} is not a mapx file!')

    _imported = self.aprx.importDocument(str(mapx))
    # Ensure that the imported document is a Map
    if not isinstance(_imported, _Map):
        self.aprx.deleteItem(_imported)
        raise ValueError(f'{mapx} is not a valid mapx file!')

    self.refresh('maps')
    return Map(_imported, parent=self)

import_pagx

import_pagx(
    pagx: Path | str, *, reuse_existing_maps: bool = True
) -> Layout

Import a pagx file into this project

PARAMETER DESCRIPTION

pagx

The path to the pagx document

TYPE: Path | str

RETURNS DESCRIPTION
Layout

A Layout parented to this project

Source code in src/arcpie/project.py
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
def import_pagx(self, pagx: Path|str, *, reuse_existing_maps: bool=True) -> Layout:
    """Import a pagx file into this project

    Args:
        pagx (Path|str): The path to the pagx document

    Returns:
        (Layout): A Layout parented to this project
    """
    pagx = Path(pagx)
    if not pagx.suffix == '.pagx':
        raise ValueError(f'{pagx} is not a pagx file!')

    _imported = self.aprx.importDocument(str(pagx), reuse_existing_maps=reuse_existing_maps)
    # Ensure that the imported document is a Layout
    if not isinstance(_imported, _Layout):
        self.aprx.deleteItem(_imported)
        raise ValueError(f'{pagx} is not a valid pagx file!')

    self.refresh('layouts')
    return Layout(_imported, parent=self)

refresh

refresh(*managers: str) -> None

Clear cached object managers

PARAMETER DESCRIPTION

*managers

Optionally limit cache clearing to certain managers (attribute name)

TYPE: *str DEFAULT: ()

Source code in src/arcpie/project.py
946
947
948
949
950
951
952
953
954
955
def refresh(self, *managers: str) -> None:
    """Clear cached object managers

    Args:
        *managers (*str): Optionally limit cache clearing to certain managers (attribute name)
    """
    for prop in list(self.__dict__):
        if prop.startswith('_') or managers and prop not in managers:
            continue # Skip private instance attributes and non-requested
        self.__dict__.pop(prop, None)

save

save() -> None

Save this project

Source code in src/arcpie/project.py
832
833
834
def save(self) -> None:
    """Save this project"""
    self.aprx.save()

save_as

save_as(path: Path | str) -> Project

Saves the project under a new name

PARAMETER DESCRIPTION

path

The filepath of the new aprx

TYPE: Path | str

RETURNS DESCRIPTION
Project

A Project representing the new project file

Note

Saving a Project as a new project will not update this instance, and instead returns a new Project instance targeted at the new file

Source code in src/arcpie/project.py
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
def save_as(self, path: Path|str) -> Project:
    """Saves the project under a new name

    Args:
        path (Path|str): The filepath of the new aprx

    Returns:
        (Project): A Project representing the new project file

    Note:
        Saving a Project as a new project will not update this instance, and instead returns a new
        Project instance targeted at the new file
    """
    if not str(path).endswith('.aprx'):
        path = Path(str(path)+'.aprx')
    else:
        path = Path(path)
    self.aprx.saveACopy(str(path))
    return Project(path)

Table

Table(
    obj: _MapType,
    parent: _MappingObject | Project | None = None,
)

Bases: MappingWrapper[Table, CIMStandaloneTable], Table

METHOD DESCRIPTION
export_lyrx

Export the layer to a lyrx file in the target directory

import_lyrx

Import the table state from an lyrx file

ATTRIBUTE DESCRIPTION
parent

The parent object for the wrapper

table

Get an arcpie.Table object from the TableLayer

TYPE: Table

unique_name

Get the longName or name of the object. Use id for any object without a name attribute

TYPE: str

uri

Get the URI for the object or the id with :NO_URI at the end

TYPE: str

Source code in src/arcpie/project.py
95
96
97
def __init__(self, obj: _MapType, parent: _MappingObject|Project|None=None) -> None:
    self._obj = obj
    self._parent = parent

parent property

parent

The parent object for the wrapper

Project -> Map -> Layer

Project -> Layout -> Map -> MapSeries

Project -> Report

The general parent/child relationships are based on how you would access the object in ArcPro. Projects have maps, maps have layers, layouts have mapseries etc.

table property

table: Table

Get an arcpie.Table object from the TableLayer

unique_name property

unique_name: str

Get the longName or name of the object. Use id for any object without a name attribute

uri property

uri: str

Get the URI for the object or the id with :NO_URI at the end

export_lyrx

export_lyrx(out_dir: Path | str) -> None

Export the layer to a lyrx file in the target directory

PARAMETER DESCRIPTION

out_dir

The location to export the lyrx to

TYPE: Path | str

Source code in src/arcpie/project.py
559
560
561
562
563
564
565
566
567
568
def export_lyrx(self, out_dir: Path|str) -> None:
    """Export the layer to a lyrx file in the target directory

    Args:
        out_dir (Path|str): The location to export the lyrx to
    """
    target = Path(out_dir) / f'{safe_name(self.longName)}.lyrx'
    # Make Containing directory for grouped layers
    target.parent.mkdir(exist_ok=True, parents=True)
    target.write_text(json.dumps(self.lyrx, indent=2), encoding='utf-8')

import_lyrx

import_lyrx(lyrx: Path | str) -> None

Import the table state from an lyrx file

PARAMETER DESCRIPTION

lyrx

The lyrx file to update this table with

TYPE: Path | str

Note

CIM changes require the APRX to be saved to take effect. If you are accessing this layer via a Project, use project.save() after importing the layerfile

Source code in src/arcpie/project.py
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
def import_lyrx(self, lyrx: Path|str) -> None:
    """Import the table state from an lyrx file

    Args:
        lyrx (Path|str): The lyrx file to update this table with

    Note:
        CIM changes require the APRX to be saved to take effect. If you are accessing this
        layer via a Project, use `project.save()` after importing the layerfile
    """
    _lyrx = LayerFile(str(lyrx))
    _lyrx_layers = {t.longName: t for t in _lyrx.listTables()}
    for table in [self]:
        _lyrx_table = _lyrx_layers.get(table.longName)
        if not _lyrx_table:
            print(f'{self.longName} not found in {str(lyrx)}')
            continue
        # Update Connection
        _lyrx_table.updateConnectionProperties(None, table.connectionProperties) # type: ignore
        _lyrx_layer_cim = _lyrx_table.getDefinition('V3')
        self.setDefinition(_lyrx_layer_cim)

Wildcard

Bases: UserString

Clarify that the string passed to a Manager index is a wildcard so the type checker knows you're getting a Sequence back

name_of

name_of(
    o: MappingWrapper[Any, Any],
    skip_uri: bool = False,
    uri_only: bool = False,
) -> str

Handle the naming hierarchy of mapping objects URI -> longName -> name

Allow setting flags to get specific names

Note

If a URI is requested and no URI attribute is available in object, 'obj.name: NO URI(id(obj))' will be returned, e.g. 'my_bookmark: NO URI(1239012093)'

Source code in src/arcpie/project.py
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
def name_of(o: MappingWrapper[Any, Any], skip_uri: bool=False, uri_only: bool=False) -> str:
    """Handle the naming hierarchy of mapping objects URI -> longName -> name

    Allow setting flags to get specific names

    Note:
        If a URI is requested and no `URI` attribute is available in object, 
        `'obj.name: NO URI(id(obj))'` will be returned, e.g. `'my_bookmark: NO URI(1239012093)'`
    """
    _uri: str|None = o.uri if not skip_uri else None
    _long_name: str|None = getattr(o, 'longName', None) # longName will identify Grouped Layers
    _name: str|None = getattr(o, 'name', None)
    _id: str = str(id(o)) # Fallback to a locally unique id (should never happen)
    if uri_only:
        return _uri or f"{id(o)}:NO_URI"
    return _uri or _long_name or _name or _id

safe_name

safe_name(name: str) -> str

Remove invalid filename characters from a string

Source code in src/arcpie/project.py
80
81
82
def safe_name(name: str) -> str:
    """Remove invalid filename characters from a string"""
    return ''.join(c for c in name if c not in INVALID_CHARS)