Select to view content in your preferred language

How to get the extent from all bookmarks in an aprx-document?

2706
3
10-09-2022 07:47 AM
bogrevy
Esri Contributor

I am trying to get the extent from all bookmarks in an aprx-document - when ArcGISA Pro is NOT open.

I am able to get the X,Y,Z and scale, but not the extent. I have tried 2 approached which both fails, and a third approach where I doubt the result.

GetBookMarks1 fails in the line "extent = mapCam.getExtent()"

GetBookMarks2 fails in the line "mv.zoomToBookmark(b)"

Does anyone have a good idea?

Is it possible to calculate the extent using the camera-properties as it is done in GetBookMarks3?

****************************************************************************

# -*- coding: utf-8 -*-

import arcpy
from arcpy._mp import Map, ArcGISProject, Camera, MapView, Bookmark
from arcpy.cim.CIMMap import CIMMap, CIMBookmark, CIMViewCamera

def GetBookMarks1(aprxDocument:str😞
    ap = ArcGISProject(aprxDocument)
    for ma in ap.listMaps():
        m:Map = ma
        mv:MapView = m.defaultView
        mapCam:Camera = mv.camera
        mapDef:CIMMap = m.getDefinition("V3")
        for bo in mapDef.bookmarks:
            b:CIMBookmark = bo
            bookmarkCam:CIMViewCamera = b.camera
            mapCam.heading = bookmarkCam.heading
            mapCam.pitch = bookmarkCam.pitch
            mapCam.roll = bookmarkCam.roll
            mapCam.scale = bookmarkCam.scale
            mapCam.X = bookmarkCam.x
            mapCam.Y = bookmarkCam.y
            mapCam.Z = bookmarkCam.z
            extent = mapCam.getExtent()
            print(f"extent: {extent}")

def GetBookMarks2(aprxDocument:str😞
    ap = ArcGISProject(aprxDocument)
    for ma in ap.listMaps():
        m:Map = ma
        mv:MapView = m.defaultView
        for bo in m.listBookmarks():
            b:Bookmark = bo
            mv.zoomToBookmark(b)
            mapCam:Camera = mv.camera
            extent = mapCam.getExtent()
            print(f"extent: {extent}")

def GetBookMarks3(aprxDocument:str😞
    ap = ArcGISProject(aprxDocument)
    for ma in ap.listMaps():
        m:Map = ma
        mapDef:CIMMap = m.getDefinition("V3")
        for bo in mapDef.bookmarks:
            b:CIMBookmark = bo
            bookmarkCam:CIMViewCamera = b.camera
            xMin = bookmarkCam.x - bookmarkCam.viewportWidth / 2
            xMax = bookmarkCam.x + bookmarkCam.viewportWidth / 2
            yMin = bookmarkCam.y - bookmarkCam.viewportHeight / 2
            yMax = bookmarkCam.y + bookmarkCam.viewportHeight / 2
            sr = arcpy.SpatialReference(mapDef.spatialReference["wkid"])
            array:arcpy.Array = arcpy.Array()
            array.add(arcpy.Point(xMin, yMin))
            array.add(arcpy.Point(xMax, yMax))
            polyline:arcpy.Polyline = arcpy.Polyline(array, sr)
            extent:arcpy.Extent = polyline.extent
            sr2 = extent.spatialReference
            print(f"extent:{extent}")

aprxDocument:str = r"C:\Users\bog\AppData\Local\Temp\ltm_publishing\LtmPub_20221007_180824\Ltm\Ltm.aprx"
# GetBookMarks1(aprxDocument)
# GetBookMarks2(aprxDocument)
GetBookMarks3(aprxDocument)
 
0 Kudos
3 Replies
VinceE
by
Frequent Contributor
Your code is hard to read because it is not formatted correctly. I suggest using the "Insert/Edit code sample" in the future, which will likely attract more responses.
 
Can you expand on what you're trying to accomplish? At face value, I don't think it is possible, or I don't understand what you're after.

I'm not 100% on this, but I don't think a bookmark can have an extent--by itself. If it could, I assume that would be a readable property of the object. I also don't think you can get extents that are related to bookmarks, UNLESS they are read from Map Frames in Layout objects (which is very possible), OR in the situation discussed below this code:

 

import arcpy

# Get APRX.
aprx = arcpy.mp.ArcGISProject(PATH)
layouts = aprx.listLayouts()

for layout in layouts:
    map_frames = layout.listElements("MAPFRAME_ELEMENT")
    for mf in map_frames:
        print(f"Map Frame: {mf.name}")
        map_obj = mf.map
        bookmarks = map_obj.listBookmarks()
        for bookmark in bookmarks:
            print(f"Bookmark: {bookmark.name}")
            mf.zoomToBookmark(bookmark)
            print(mf.camera.getExtent())

 

But it seems like the only way to iterate through bookmarks on Map objects would be by using the "activeView"... which I don't think is accessible outside of Pro; from the MapView docs:

"The MapView returned from the activeView property is the only way to change the extent of the Camera associated with a map view."

If we can't change the Camera, I don't think updating the bookmarks would be possible, and therefore we can't get an extent that changes along with bookmark updates.

My guess for why this might be is that while the application is running, there can be an activeView, the dimensions of which are partly determined by how big your TOC, Catalog Pane, etc. are. When the software is not open, I don't think each APRX stores that information regarding window/sidebar size. So, a "Map" object alone (you need the "MapView") can't have an extent, and therefore, neither can a bookmark; not without it currently being open and used. Unless, that map is embedded on a map frame that does have specific boundaries (the map frame box on the layout sheet).

Just my thoughts based on the documentation.

0 Kudos
bogrevy
Esri Contributor

@VinceE , point taken regarding using the "Insert/Edit code sample".

I needed to get extent of the bookmarks from the aprx-document , because I want to create the same bookmarks in a webmap - it was part of an automated featureservice publishing process.

The code below does the trick

 

 

# -*- coding: utf-8 -*-

import arcpy, json
from arcpy._mp import Map, ArcGISProject
from arcpy.cim.CIMMap import CIMMap, CIMBookmark, CIMViewCamera

def GetBookMarks3(aprxDocument:str, wkid:int) -> dict:
    outputSR = arcpy.SpatialReference(wkid)
    mapkey = "Maps"
    result:dict = {mapkey:{}}
    ap = ArcGISProject(aprxDocument)
    for ma in ap.listMaps():
        m:Map = ma
        mapDef:CIMMap = m.getDefinition("V3")
        sr = arcpy.SpatialReference(mapDef.spatialReference["wkid"])
        bookmarks:list = []
        for bo in mapDef.bookmarks:
            b:CIMBookmark = bo
            cam:CIMViewCamera = b.camera
            xMin = cam.x - cam.viewportWidth / 2
            xMax = cam.x + cam.viewportWidth / 2
            yMin = cam.y - cam.viewportHeight / 2
            yMax = cam.y + cam.viewportHeight / 2
            array:arcpy.Array = arcpy.Array()
            array.add(arcpy.Point(xMin, yMin))
            array.add(arcpy.Point(xMax, yMax))
            polyline:arcpy.Polyline = arcpy.Polyline(array, sr).projectAs(outputSR)
            extent:arcpy.Extent = polyline.extent
            bookmark:dict = {"name":b.name, "extent":json.loads(extent.JSON)}
            bookmarks.append(bookmark)
        result[mapkey][m.name]=bookmarks
    return result

aprxDocument:str = r"<Your Path>"
wkid:int = 4326
bookmarks = GetBookMarks3(aprxDocument, wkid)
bookmarks_string = json.dumps(bookmarks, indent=4)
print(bookmarks_string)

 

 

 

VinceE
by
Frequent Contributor

Thanks for the response, I stand corrected! This is very interesting. I have not explored Esri's CIM nearly enough, clearly.

To visualize this for myself, I took some derived bookmark extents (from your code, and using my own scratch APRX) and built them as polygons in a new feature class. Sure enough, the geometry of the new polygons matches the original camera (viewport? I might be confusing the terminology) at the time the bookmark was saved, taking into account the limitations on map size (on screen) based on surrounding windows like Table of Contents and/or docked Catalog panes.

As an example, the polygon on the left represents the extent of a bookmark saved with a normal, more wide-screen view of the map. The polygon on the right represents a bookmark that was saved when I expanded my docked toolbars way out, limiting the actual view of the map.

Thanks again for the reply.

VinceE_0-1669766969728.png