I'm trying to automate the creation of a layout that includes a scale bar.
I want the size determined by the division value (2000 when the layout is first created), so I need to set the fitting strategy property to "Adjust width". This is what it would look like ideally:
I'm able to make a scale bar, but I'm having a hard time setting it up properly. Currently it still has "Adjust division value" as the fitting strategy, and is in miles. In fact nothing is updating after the line that originally creates the bar. Looks like this:
This is what I have so far:
sbName = 'Double Alternating Scale Bar 1'
sbStyle = aprx.listStyleItems('ArcGIS 2D', 'Scale_bar', sbName)[0]
sb = lyt.createMapSurroundElement(geometry = arcpy.Point(1, 9), mapsurround_type = 'Scale_bar', mapframe = main_map_frame, style_item = sbStyle, name = 'New Scale Bar')
lyt_cim = lyt.getDefinition('V3')
for elm in lyt_cim.elements:
if elm.name == 'New Scale Bar':
elm.FittingStrategy = "AdjustWidth"
elm.Division = 2000
elm.Divisions = 1
elm.Subdivisions = 1
elm.MarkFrequency = "DivisionsAndFirstMidpoint"
elm.MarkPosition = "AboveBar"
elm.Units = "Feet"
lyt.setDefinition(lyt_cim)
aprx.save()
I'm referring mostly to these sources:
https://pro.arcgis.com/en/pro-app/3.3/arcpy/mapping/mapsurroundelement-class.htm
https://pro.arcgis.com/en/pro-app/latest/sdk/api-reference/topic4880.html
Solved! Go to Solution.
You're on the right track.
I've found better success with using normal arcpy.mp as much as possible to get the element I want, THEN getting its definition.
sb_cim = sb.getDefinition('V3')
sb_cim.fittingStrategy ="AdjustFrame"
sb_cim.division = 2000
sb_cim.divisions = 1
sb_cim.subdivisions = 1
sb_cim.markFrequency = "DivisionsAndFirstMidpoint"
sb_cim.markPosition = "AboveBar"
sb_cim.units = "Feet"
sb_cim.unitLabel = "Feet"
sb.setDefinition(sb_cim)
CIM is super tricky and is a great example of how naming in one space does not reflect naming behind the scenes. For example, what we see as "Adjust Width" is actually "AdjustFrame".
This page is a great resource (although not infallible) https://github.com/Esri/cim-spec/blob/809fb365d8d204d1fbdc51b3fab5bd17d88de2d1/docs/v3
Go to the general idea of what you want (Layout element--> Layout page), then search for what you want. I looked for "ScaleBar", which took me here. I then noticed that there was a link in the description for fittingStrategy to a list of all acceptable values. (idk why these pictures are so blurry, sorry)
You're on the right track.
I've found better success with using normal arcpy.mp as much as possible to get the element I want, THEN getting its definition.
sb_cim = sb.getDefinition('V3')
sb_cim.fittingStrategy ="AdjustFrame"
sb_cim.division = 2000
sb_cim.divisions = 1
sb_cim.subdivisions = 1
sb_cim.markFrequency = "DivisionsAndFirstMidpoint"
sb_cim.markPosition = "AboveBar"
sb_cim.units = "Feet"
sb_cim.unitLabel = "Feet"
sb.setDefinition(sb_cim)
CIM is super tricky and is a great example of how naming in one space does not reflect naming behind the scenes. For example, what we see as "Adjust Width" is actually "AdjustFrame".
This page is a great resource (although not infallible) https://github.com/Esri/cim-spec/blob/809fb365d8d204d1fbdc51b3fab5bd17d88de2d1/docs/v3
Go to the general idea of what you want (Layout element--> Layout page), then search for what you want. I looked for "ScaleBar", which took me here. I then noticed that there was a link in the description for fittingStrategy to a list of all acceptable values. (idk why these pictures are so blurry, sorry)
Thank you!
Those resources are very helpful.
Your script got it most of the rest of the way there, the only thing I had to change was setting the units equal to {'uwkid': 9002}
I couldn't figure out how to format that part correctly with the WKID value so I just manually set the units in the scale bar and did this to check what it looked like:
sb_cim = sb.getDefinition('V3')
sb_cim.units
The CIM API in arcpy is atrocious. I've done a few personal hacks to make it properly typed, but it's a pain to maintain and is technically an unsupported version of arcpy.
Hopefully they implement my request for CIM type hinting and literals soon. My 3+ years of hounding them had finally got them to add literals for mp, da, and some arcpy.Parameter attributes lol. CIM should be next, but the whole module is autogenerated by some archaic internal Python 2 script or god forbid bash script. So that one will require either manual maintenance or a total rewrite of that CI/CD pipeline that builds the CIM module.
What's nice about Python 3 is that the typing system doesn't really care about recursive typing (since they aren't evaluated at run time), so you don't need to create a recursive class definition anymore and can instead inject a class into another class that also has an attribute of that original class (something common in json, but not common in strictly typed languages)
Glad it worked out! I had to do the same thing to figure out the fitting strategy.