ArcPy getExtent() on rotated Map are different for MapView versus MapFrame?

615
4
Jump to solution
04-05-2020 01:32 PM
VinceE
by
New Contributor III

My goal is to create a python script that generates a current view polygon in ArcGIS Pro (2.5). I am testing the code below by just running it in the Python Window. I'd like this to function much like all the "data frame to polygon" tools for ArcGIS Desktop.

When looking at a "Map" tab, I use the following code to get the extent of the view (not a specific layer, the whole view of the frame):

current_project = arcpy.mp.ArcGISProject("CURRENT")
active_object = current_project.activeView
extent = active_object.camera.getExtent()
print(extent)

As I understand it, activeView returns a MapView object, and camera.getExtent() gets an Extent. The printed extent results are the same for a map with a rotation of 0 and a rotation 45. This is what I want, as I'd like the output polygons to be the same size (if the map is at the same scale) regardless of rotation, and then I just rotate the final output polygon if I need to using the value from camera.heading. The end result of the larger script, ran once at "0 rotation" and once at "45 rotation," are two equal-sized polygons, just rotated accordingly.

Map at 0 rotation, Extent:   (675,978 728,153) (676,606 728,496)

Map at 45 rotation, Extent: (675,978 728,153) (676,606 728,496)

However, this does not seem to work with a MapFrame from a Layout. When looking at a "Layout" tab, here is the code:

current_project = arcpy.mp.ArcGISProject("CURRENT")
active_object = current_project.activeView
map = active_object.listElements("MAPFRAME_ELEMENT")[0]
extent = map.camera.getExtent()
print(extent)

In this case, there is the extra step of getting the MapFrame from the Layout, as activeView returns a Layout object. I can't tell if if is possible to get a MapView from a Layout. When printed, the results of a map at 0 and 45 rotation are as follows (doesn't matter that they are different from above, the area may have been different, I only care that they are different from one another):

Map at 0 rotation, Extent:   (676,162 728,233) (676,437 728,379)

Map at 45 rotation, Extent: (676,151 728,157) (676,448 728,455)

Since the initial size of the "Extents" are different, I get different sized polygons for the rotated and non-rotated versions. The images attached show what all this looks like in the first (map tab) and second (layout tab) cases.

getExtent() appears to do different things if it is called on a MapView versus a MapFrame. For a MapView, rotation does not appear to impact the extent; in a MapFrame, rotation does appear to impact the extent. Is there a better approach to getting the visible extent boundaries while looking at a layout? Or is there a method of obtaining a MapView from a MapFrame or Layout object when I'm working in a Layout tab so I avoid this issue?

0 Kudos
1 Solution

Accepted Solutions
DanPatterson_Retired
MVP Esteemed Contributor

Extent to me (L)eft, (B)ottom, (R)ight, (T)op  LBRT, from which you can derive the width, height, and the 4 corners.

To me, when a map frame is rotated, you are rotating the axes (x, y) from their normal position (ie the old X-Y graph/axis). 

Consider an "axis-aligned" rectangle, which means that the L,R are parallel to the Y axis and B,T are parallel to the X axis. 

  • If you rotate the rectangle within  XY space then the extent must change because the position of the 4 corners of the rectangle change... try it.
  • if the rotated rectangle's XY space is rotated by an equal amount to the above, then the extent returns to its original values (in terms of width and height... position of the corners is less relevant)

So the 'extent' is the correct term for the 'normal' XY axis orientation and rotated XY frame.... the only thing that has changed is the orientation of the reference framt.

When you mention layout, there was never any mention of layout's reference frame changing... maybe the map is rotated, but that rotation doesn't appear to the layout's frame space', so it assumes that it is getting a regular old XY 'graph' space and simply translates map coordinates into paper units.  So in my mind... if you rotate a map frame and don't "tell" the layout what you have done, it can only assume that the "extent" is derived in the same fashion.

Sorry if I over-simplified things.  A classic example of application of this space reorientation deals with whether points are to the left or right of a line (eg point in polygon searches).  Instead of a whole bunch of stupid calculations to determine whether it is true or not, you simply rotate "everything" (points and the line) so that the line is either parallel to the X OR Y axis .... a series of calculations now becomes simple equality checks (ie <, =, > )

View solution in original post

4 Replies
DanPatterson_Retired
MVP Esteemed Contributor

both are images are correct.  If your issue is with the extent for the layout, then you should use extents that are appropriate to the layout

VinceE
by
New Contributor III

Thanks Dan, are you able to expand on this? My goal is to get the coordinates of the "view" while in a Layout so I can eventually generate a polygon that matches up exactly; perhaps I'm using the term "Extent" in a technically incorrect way here. What do you mean "extents that are appropriate to the layout?"

I'm sure getExtent() is doing what it is supposed to, I'm just not sure why I'm seeing different behavior when it is called on a rotated MapView vs. a rotated MapFrame. It's not clear to me why the size of the extent is larger when a MapFrame's Map is rotated, but not when a MapView's Map is rotated. I've looked through the documentation, and still not following some aspect of this, so I my be confusing some terminology, etc.

I'd appreciate any further guidance you can offer.

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

Extent to me (L)eft, (B)ottom, (R)ight, (T)op  LBRT, from which you can derive the width, height, and the 4 corners.

To me, when a map frame is rotated, you are rotating the axes (x, y) from their normal position (ie the old X-Y graph/axis). 

Consider an "axis-aligned" rectangle, which means that the L,R are parallel to the Y axis and B,T are parallel to the X axis. 

  • If you rotate the rectangle within  XY space then the extent must change because the position of the 4 corners of the rectangle change... try it.
  • if the rotated rectangle's XY space is rotated by an equal amount to the above, then the extent returns to its original values (in terms of width and height... position of the corners is less relevant)

So the 'extent' is the correct term for the 'normal' XY axis orientation and rotated XY frame.... the only thing that has changed is the orientation of the reference framt.

When you mention layout, there was never any mention of layout's reference frame changing... maybe the map is rotated, but that rotation doesn't appear to the layout's frame space', so it assumes that it is getting a regular old XY 'graph' space and simply translates map coordinates into paper units.  So in my mind... if you rotate a map frame and don't "tell" the layout what you have done, it can only assume that the "extent" is derived in the same fashion.

Sorry if I over-simplified things.  A classic example of application of this space reorientation deals with whether points are to the left or right of a line (eg point in polygon searches).  Instead of a whole bunch of stupid calculations to determine whether it is true or not, you simply rotate "everything" (points and the line) so that the line is either parallel to the X OR Y axis .... a series of calculations now becomes simple equality checks (ie <, =, > )

VinceE
by
New Contributor III

Thanks Dan, this is very helpful. I assumed something like this was happening, but couldn't quite get there. The disconnect between the rotation of the actual map object versus the map frame/layout object, and how the XY axis is re-oriented, is starting to make sense. That being said, I'm not entirely sure how to bridge that disconnect--i.e. communicate to the mapframe object/layout that the reference frame in the actual map object has changed orientation. As a workaround, I have done the following in the script:

For this particular use case, since I just need the coordinates of the map corners as visible in a map frame on a layout:

 mapframe = active_object.listElements("MAPFRAME_ELEMENT")[0]      # active_object derived from aprx.activeView
 heading = mapframe.camera.heading         # Read original rotation of map frame
 mapframe.camera.heading = 0               # Change rotation property of map frame's camera to 0
 extent = mapframe.camera.getExtent()      # Get extent of the camera for the now un-rotated map
 mapframe.camera.heading = heading         # Set the rotation of the map back to original rotation

This gives me an extent from an un-rotated map, from which I can derive coordinate values, that will match the actual shape of the map view within the layout's map frame. Then, I can use the preserved rotation value in the trig functions that are required to actually rotate the corner points so that my new polygon aligns to the rotated map.

This is perhaps a clunky workaround, so suggestions welcome, but it appears to do what I need.

0 Kudos