Hello I'm working on automating the process of making maps using ArcPy and an issue I'm running into is trying to make inset maps. I've successfully used ArcPy to find clusters of locations using DBSCAN, but I want to make an inset map, along with an extent indicator using ArcPy. Is this possible?
I just did a project with 3 inset maps. The project has four maps, "Main Map", "Overview 1".. etc
There is one layout, and it has frames for the main map and each overview map.
There is a script tool in the projects toolbox that I run to (1) set up the layout. I am also using a map series, so the script tool tries to pull in the current map series page as the default but you can enter the name for a page. When I run it, it changes the contents of the overviews to highlight and set the extent.
Some of the maps don't need all three overviews so on maps that don't need all of them the script makes those frame elements invisible.
Execution code
"""
This is the code that you see in the ArcGIS Pro toolbox, in the Properties->Execution box.
This file gets reloaded into the script tool in ArcGIS Pro every time you save it.
"""
import sys
from importlib import reload
import arcpy
sys.path.append("K:/Taxmaps/Toolbox")
import cc_taxmaps
reload(cc_taxmaps)
__version__ = "1.0"
if __name__ == "__main__":
arcpy.AddMessage(f"ExportTaxmapTool {__version__}")
# Get the parameter values from ArcGIS Pro
layoutName = arcpy.GetParameterAsText(0)
pageName = arcpy.GetParameterAsText(1)
can_table = arcpy.GetParameterAsText(2)
outputFolder = arcpy.GetParameterAsText(3)
projectFile = 'CURRENT'
tm = cc_taxmaps.BuildTaxMap(projectFile, layoutName, can_table)
try:
tm.ConfigureLayout(pageName)
tm.ExportPdf(outputFolder)
except Exception as e:
arcpy.AddError(f"Export failed. {e}")
# That's all!
Validation code
import os
import arcpy
# I struggled and failed to get these constants to load from a separate file.
#
# Map names we need to have
MAIN_MAP = "Main Map"
#
# Name of the layer that should appear in the MAIN_MAP
CANCELLED_NUMBERS_TABLE = "cancelled_numbers"
#
# Use these settings if none were provided
DEFAULT_LAYOUT = "Taxmap_18x24"
DEFAULT_CANCELLED_NUMBERS_TABLE = 'W:\\ALL\\GIS\CONNECTIONS\\cc-sqlservers_WINAUTH.sde\\cancelled_numbers'
DEFAULT_PDF_FOLDER="C:\\Temp\\pdflib"
DEFAULT_MAP_INDEX = 'W:\\ALL\\GIS\CONNECTIONS\\cc-sqlservers_WINAUTH.sde\\taxlots_fd\\mapindex'
class ToolValidator:
# Class to add custom behavior and properties to the tool and tool parameters.
def __init__(self):
# Set self.params for use in other validation methods.
self.params = arcpy.GetParameterInfo()
def initializeParameters(self):
# Customize parameter properties. This method gets called when the tool is opened.
"""
0 layout
1 page name
2 cancel table
3 output
"""
project = arcpy.mp.ArcGISProject('CURRENT')
layout = None
layoutName = self.params[0].value # This is always just a string
if not layoutName:
# If no layout is selected, pick the first one in this project
layout = project.listLayouts(wildcard='*')[0]
self.params[0].value = layout.name
ms = None
try:
layout = project.listLayouts(wildcard=layoutName)[0]
ms = layout.mapSeries
except Exception as e:
self.params[0].setErrorMessage("Can't find layout or map series.")
# In the layout, figure out which page is selected, if any.
self.params[1].clearMessage()
self.params[1].value = ''
try:
self.params[1].value = ms.pageRow.PageName
except Exception as e:
self.params[1].setErrorMessage("Can't find pageName.")
# Cancelled numbers table
self.params[2].clearMessage()
self.params[2].value = ''
try:
# Find the cancelled numbers table in the map.
map = project.listMaps(wildcard=MAIN_MAP)
self.params[2].value = map.listTables(wildcard=CANCELLED_NUMBERS_TABLE)[0]
except Exception as e:
self.params[2].setErrorMessage("Can't find table.")
self.params[3].clearMessage()
self.params[3].value = DEFAULT_PDF_FOLDER
return
def updateParameters(self):
# Modify the values and properties of parameters before internal
# validation is performed.
# Make sure can_table exists
if not self.params[2].valueAsText:
self.params[2].value = DEFAULT_CANCELLED_NUMBERS_TABLE
return
def updateMessages(self):
# Modify the messages created by internal validation for each tool
# parameter. This method is called after internal validation.
# Make sure the selected page name exists.
layoutName = self.params[0].valueAsText
pageName = self.params[1].valueAsText
try:
project = arcpy.mp.ArcGISProject('CURRENT')
layout = project.listLayouts(wildcard=layoutName)[0]
except Exception as e:
self.params[1].setErrorMessage("Blimey, can't find layout")
layout = None
if layout:
ms = None
try:
ms = layout.mapSeries
except Exception as e:
self.params[1].setErrorMessage("Blimey, can't find map series.")
if pageName:
try:
pageNum = ms.getPageNumberFromName(pageName)
except Exception as e:
pageNum = None
if not pageNum:
self.params[1].setErrorMessage("Blimey, I don't see that page name!")
if not arcpy.Exists(self.params[2].valueAsText):
self.params[2].setWarningMessage("Blimey, fix this or I won't be able to find any cancelled numbers!")
# Make sure the output folder exists
if not os.path.exists(self.params[3].valueAsText):
self.params[3].setErrorMessage("Blimey, I can't do anything without a folder!")
return
def isLicensed(self):
# Set whether the tool is licensed to execute.
return True
# def postExecute(self):
# # This method takes place after outputs are processed and
# # added to the display.
# return
"Business logic" from cc_taxmaps.py
# Search for polygons
# Use them to set extent
# Generate a PDF for each extent, named after the polygon and with a numeric prefix. e.g. "01_shively.pdf"
#
# Set title to name from polygon
# Set extent polygon in key map
import sys
import os
import re
from importlib import reload
import datetime
from pypdf import PdfWriter
import arcpy
sys.path.append("K:/Taxmaps/Toolbox")
import ormapnum
reload(ormapnum)
__version__ = '1.0'
# Other scalebars have to have prefix followed by scale, eg "Scalebar 24000" for 1:24000 maps
MAXCANCELLEDROWS = 15 # Go to 8 point font if # of rows exceeds this
def make_table(cancellations:list, columns:int) -> tuple:
"""Break the list of cancellations into 'n' columns and return them.
Args:
cancellations (list): list of cancellations
columns (int): number of columns
Returns:
(int, list): int is the height of the tallest column and list contains a list for each column.
"""
col = "" # Each column is a string with line breaks in it.
maxy = int((len(cancellations)+columns-1)/columns) # max items to put in one column
i = 0
columns = []
for item in cancellations:
col += str(item).ljust(6) + "\n"
i += 1
if not i % maxy:
columns.append(col)
col = "" # we're done with this
if col:
# some left overs
columns.append(col)
return maxy,columns
def load_cancellations(mapnumber: str, tablename: str) -> list:
"""Load the cancelled numbers table and return it as a list.
"""
cancellations = []
try:
with arcpy.da.SearchCursor(in_table=tablename,
field_names=["MapNumber","Taxlot"],
where_clause=f"MapNumber='{mapnumber}'") as rows:
cancellations = [row[1] for row in rows]
except Exception as e:
arcpy.AddError(f"Could not load cancelled numbers table. {e}")
return cancellations
def sort_taxlots(mylist:list) -> list:
"""Sorts a list of taxlots. Deals with the weird suffixes like "M1"
and sorts numerically (e.g. 100 is greater than 20)
Args:
mylist (list): list to be sorted
Returns:
list: sorted list
"""
d = dict()
for orig in mylist:
# Taxlots can be a simple integer like "4900" or a string with something on the end like "400M1"
# and they should sort into a proper list as if they were all just integers.
mo = re.search(r'^(\d+).*$', orig)
s = orig
try:
# Add some leading zeros if the integer part is less than 8 digits
# so, "100M1" will become "00000100M1"
n = mo.group(1)
s = '0' * (8 - len(n)) + orig
except Exception as e:
print(e)
d[s] = orig # This will de-duplicate too.
# Sort the dictionary.
# sorted() returns a list of keys.
# Make the dict into a list of values
# return the new list
return [d[k] for k in sorted(d)]
class BuildTaxMap(object):
# map the scale of the current map to the scalebar element to use
scales = {
120 : 'Scalebar 240',
240 : 'Scalebar 240',
360 : 'Scalebar 240',
480 : 'Scalebar 240',
600 : 'Scalebar 600',
720 : 'Scalebar 600',
1200 : 'Scalebar 1200',
2400 : 'Scalebar 600',
4800 : 'Scalebar 600',
9600 : 'Scalebar 24000', # no examples; untested
12000 : 'Scalebar 24000', # no examples; untested
24000 : 'Scalebar 24000',
}
def __init__(self, projectFile, layoutName, can_table) -> None:
self.project = arcpy.mp.ArcGISProject(projectFile)
self.cancelled_taxlots_table = can_table
self.layout = self.project.listLayouts(wildcard=layoutName)[0]
self.ms = self.layout.mapSeries
self.orm = None
self.pageNames = None
#arcpy.AddMessage(f"Layout name={self.layout.name}")
# Make a dictionary of all the scalebar elements in this layout.
self.scalebar_elements = {}
for elem in self.layout.listElements(element_type="MAPSURROUND_ELEMENT", wildcard="Scalebar*"):
name = elem.name
self.scalebar_elements[name] = elem
return
def findPageNum(self, pageName: str) -> int:
"""Find the page number for a given pageName. Map series has to be set.
Args:
pageName (str): A page name from the map series.
Returns:
pageNum (int) or None
"""
pageNum = None
if pageName:
try:
pageNum = self.ms.getPageNumberFromName(pageName)
except Exception as e:
pageNum = None
return pageNum
def range(self, firstPageName=None, lastPageName=None) -> list:
"""Return a list of page names
Be careful using first and last, because the sort order is not
what you expect. Leave them empty to get a complete list.
Args:
firstPageName (str): start of range, use first page if None
lastPageName (str): end of range, use last page if None
Returns:
list: page names found
"""
pageNames = list()
layer = self.ms.indexLayer
# Cartographers are not maintaining PageNumber
# which is why we can't use it to sort the table.
#fields = ['PageNumber','PageName']
fields = ['MapNumber','PageName'] # IGNORE unmaintained PageNumber
started = False
if not firstPageName:
started = True # Begin at the first page
# The table is not automatically sorted out,
# we have to read it and sort it before finding our fields
if not self.pageNames: # Only hit the database once
# Create a dictionary with the pageNumber as the key
# so that when we sort it, the items end up in the correct order.
# Then generate a sorted list of pageNames from that and cache it.
d = {}
i = 0
with arcpy.da.SearchCursor(layer,fields) as rows:
for row in rows:
if row[0] in d:
arcpy.AddMessage(f"Duplicate pageNumber {row[0]}, {row[1]}!!")
d[row[0]] = row[1]
i += 1
self.pageNames = [d[i] for i in sorted(d)]
print(len(d), len(self.pageNames)) # sanity check, should be 1082 (or more)
for pageName in self.pageNames:
if pageName == firstPageName:
started = True
if started:
pageNames.append(pageName)
if pageName == lastPageName:
break
return pageNames
def find_scalebar(self, mapscale: int) -> arcpy._mp.MapSurroundElement:
"""Given a mapscale like 24000, find the scalebar to use with this map.
Args:
mapscale (int): Scale from a known set; 1200, 4800, etc;
if there is no match, returns the default which is the scalebar on the layout
Returns:
arcpy._mp.MapSurroundElement: An element from the layout we're working with.
"""
sb = self.scalebar_elements["Scalebar"] # The scalebar on the layout MUST be named "Scalebar"!!
try:
sb = self.scalebar_elements[self.scales[mapscale]]
except KeyError:
print("scalebar key error")
pass
return sb
def setPlotDate(self) -> None:
# Set plot date (because the dateExported property fails!)
d = datetime.datetime.now()
plotDate = d.strftime("PLOT DATE: %x")
text_element = self.layout.listElements(element_type='Text_Element', wildcard="Plot Date")[0]
text_element.text = plotDate
#print(plotDate)
return
def setCountyOverview(self) -> None:
# note, extent of the county map never needs to change,
# the whole county always in view
county_overview_map = self.project.listMaps(wildcard="County Overview Map")[0]
# Highlight the township
highlight_layer = county_overview_map.listLayers(wildcard="Highlight")[0]
highlight_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.tr, 'isActive': True}])
#arcpy.AddMessage(f"County overview Highlight query={highlight_layer.listDefinitionQueries()}")
# Set def query to turn off the township so highlight label shows (instead of both showing)
township_layer = county_overview_map.listLayers(wildcard="Townships")[0]
township_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': f"NOT({self.tr})", 'isActive': True}])
#arcpy.AddMessage(f"County overview Township query={township_layer.listDefinitionQueries()}")
return
def setTownshipOverview(self, frame) -> None:
# Note that pagename queries don't work here, we have to use definition queries.
township_overview_map = self.project.listMaps(wildcard="Township Overview Map")[0]
# Highlight the section
highlight_layer = township_overview_map.listLayers(wildcard="Highlight")[0]
highlight_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.trs, 'isActive': True}])
#arcpy.AddMessage(f"Township Overview Highlight query={highlight_layer.listDefinitionQueries()}")
# Outline the township
township_layer = township_overview_map.listLayers(wildcard="Township")[0]
township_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.tr, 'isActive': True}])
#arcpy.AddMessage(f"Township Overview Township query={township_layer.listDefinitionQueries()}")
# Turn off the sections outside the current section.
# Turn off the selected section in "Sections" (highlight layer will BOLD it the label.)
sections_layer = township_overview_map.listLayers(wildcard="Sections")[0]
sections_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.trsonly, 'isActive': True}])
#arcpy.AddMessage(f"Township Overview Sections query={sections_layer.listDefinitionQueries()}")
background_layer = township_overview_map.listLayers(wildcard="Background")[0]
background_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.notsection, 'isActive': True}])
#arcpy.AddMessage(f"Township Overview Background query={background_layer.listDefinitionQueries()}")
# Adjust extent to center on the township (not the section or taxmap)
try:
rows = arcpy.da.SearchCursor(township_layer, ['SHAPE@', 'tr'])
row = rows.next() # There should only be one feature if the definition query actually worked?
extent = row[0].extent
arcpy.AddMessage(f"In township map, using extent of township {row[1]}")
padding = (extent.XMax - extent.XMin) / 10
new_extent = arcpy.Extent(extent.XMin - padding, extent.YMin - padding,
extent.XMax + padding, extent.YMax + padding,
None, None, None, None,
extent.spatialReference)
frame.camera.setExtent(new_extent)
frame.visible = True
except Exception as e:
arcpy.AddMessage(f"Can't show township overview. {e}")
pass
finally:
del rows # release the lock
return
def setSectionOverview(self, frame) -> None:
# Page Query on Current Taxmap selects the correct taxmap shape
section_overview_map = self.project.listMaps(wildcard="Section Overview Map")[0]
highlight_layer = section_overview_map.listLayers(wildcard="Highlighted Section")[0]
highlight_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': self.trs, 'isActive': True}])
#arcpy.AddMessage(f"Highlight query={highlight_layer.listDefinitionQueries()}")
# Turn off the selected section in "Sections" (highlight layer will BOLD it the label.)
sections_layer = section_overview_map.listLayers(wildcard="Sections")[0]
sections_layer.updateDefinitionQueries(definitionQueries=[{'name': 'TaxmapQuery', 'sql': f"NOT({self.trs})", 'isActive': True}])
#arcpy.AddMessage(f"Section Overview Sections query={sections_layer.listDefinitionQueries()}")
# Adjust extent to center on the section (not the Current Taxmap)
try:
rows = arcpy.da.SearchCursor(highlight_layer, ['SHAPE@', 'SECTION'])
row = rows.next() # There should only be one feature if the definition query actually worked?
extent = row[0].extent
padding = (extent.XMax - extent.XMin) / 10
new_extent = arcpy.Extent(extent.XMin - padding, extent.YMin - padding,
extent.XMax + padding, extent.YMax + padding,
None, None, None, None,
extent.spatialReference)
arcpy.AddMessage(f"In section map, using extent of section {row[1]}")
frame.camera.setExtent(new_extent)
frame.visible = True
except Exception as e:
arcpy.AddMessage(f"Can't show section overview. {e}")
pass
finally:
del rows # release the lock
return
def adjust_scalebar_element(self):
try:
scale = self.ms.pageRow.MapScale
settings = self.find_scalebar(scale) # These are the settings we want
sb_element = self.scalebar_elements["Scalebar"] # This is the one we see
#print(scale, settings.name, sb_element.elementWidth)
if settings != sb_element:
# Copy settings from desired scalebar
# Some things simply are ignored if you set them on the element;
# you have to set them in the CIM definition.
new_cim = settings.getDefinition("V3")
sb_cim = sb_element.getDefinition("V3")
sb_cim.unitLabel = new_cim.unitLabel
sb_cim.units = new_cim.units
sb_element.setDefinition(sb_cim)
sb_element.elementWidth = settings.elementWidth
#print(sb_element.elementWidth)
except Exception as e:
arcpy.AddError(f"No scale set for this map. {e}")
pass
return
def populate_cancel_table(self, cancellations:list) -> None:
# Find the cancel table elements and put them in order
elements = self.layout.listElements(element_type='Text_Element', wildcard="can_col*")
ncols = len(elements)
column_elements = [None] * ncols # Make a list of the right size
for e in elements:
mo = re.search('^can_col(\d+)$', e.name)
if mo:
n = int(mo.group(1))-1
column_elements[n] = e
e.text = " " # This element has some text in it (event just a single space) so ArcMap does not "lose" it.
# Fill in the table in an aesthetically pleasing fashion.
maxy, table = make_table(cancellations, ncols)
# Adjust the font size of the table according to the number of rows
fontsize = 10
if maxy > MAXCANCELLEDROWS:
fontsize = 8
x = 0
for column in table:
column_elements[x].text = column
column_elements[x].fontSize = fontsize
x += 1
return
def ConfigureLayout(self, pageName) -> None:
""" Change the current layout to set it up for the named page. """
self.pageName = pageName
pageNum = self.ms.getPageNumberFromName(pageName)
# Fix sundry metadata (titles etc)
self.setPlotDate()
self.ms.currentPageNumber = pageNum
pageRow = self.ms.pageRow
# Parse the ormapnum from the current page to break out all its fields
self.orm = ormapnum.ORMapNum()
self.orm.unpack(pageRow.ORMapNum)
cancellations = load_cancellations(self.orm.dotted, self.cancelled_taxlots_table)
self.tr = f"TR = '{self.orm.township}{self.orm.township_dir}{self.orm.range}{self.orm.range_dir}'"
self.trs = f"TOWN = '{self.orm.township}' AND RANGE = '{self.orm.range}' AND SECTION = '{self.orm.section}'"
self.trsonly = f"(TOWN = '{self.orm.township}' AND RANGE = '{self.orm.range}') AND NOT({self.trs})"
self.notsection = f"NOT(TOWN = '{self.orm.township}' AND RANGE = '{self.orm.range}')"
top_title = self.layout.listElements(element_type='Text_Element', wildcard="Top Title")[0]
bottom_title = self.layout.listElements(element_type='Text_Element', wildcard="Bottom Title")[0]
plss_description = self.layout.listElements(element_type='Text_Element', wildcard="PLSS Description")[0]
#print(f"short title=\"{self.orm.shortmaptitle}\"" long title=\"{self.orm.longmaptitle}\"")
top_title.text = bottom_title.text = self.orm.shortmaptitle
plss_description.text = self.orm.longmaptitle
self.setCountyOverview()
township_overview_frame = self.layout.listElements(element_type="MAPFRAME_ELEMENT", wildcard="Township Overview Frame")[0]
township_overview_frame.visible = False
section_overview_frame = self.layout.listElements(element_type="MAPFRAME_ELEMENT", wildcard="Section Overview Frame")[0]
section_overview_frame.visible = False
if self.orm.section:
self.setTownshipOverview(township_overview_frame)
self.setSectionOverview(section_overview_frame)
self.adjust_scalebar_element()
# Fix up the cancelled numbers table, or hide it if it's empty.
can_table = self.layout.listElements(element_type='GROUP_ELEMENT', wildcard="Cancelled Numbers Table")[0]
if len(cancellations):
cancellations = sort_taxlots(cancellations)
self.populate_cancel_table(cancellations)
can_table.visible = True
# Position the table based on which overview maps are visible
if not township_overview_frame.visible:
can_table.elementPositionY = township_overview_frame.elementPositionY
elif not section_overview_frame.visible:
can_table.elementPositionY = section_overview_frame.elementPositionY
else:
can_table.elementPositionY = section_overview_frame.elementPositionY - section_overview_frame.elementHeight - 0.20
#arcpy.AddMessage(f"can_table y-pos set to {can_table.elementPositionY}")
else:
can_table.visible = False
return
def show_elements(self) -> None:
""" This is just to help in development """
# Find all the layout elements
main_elements = self.layout.listElements(element_type="MAPFRAME_ELEMENT", wildcard="*")
print("Map Frames:")
for e in main_elements:
print(e.name)
print()
print("Text Elements:")
text_elements = self.layout.listElements(element_type='Text_Element', wildcard="*")
for e in text_elements:
print(e.name)
print()
print("Scale bars")
scalebar_elements = self.layout.listElements(element_type='MAPSURROUND_ELEMENT', wildcard="scale*")
for e in scalebar_elements:
print(e.name)
print()
return
def ExportPdf(self, outputFolder: str) -> bool:
"""Generate a Taxmap PDF file.
Args:
outputFolder (_type_): An existing folder to hold output files.
Returns:
bool: True if PDF was successfully written.
"""
# Generate a PDF without metadata.
try:
# Export the map to the scratch folder
scratch_output_pathname = os.path.join(arcpy.env.scratchFolder, self.orm.filename + "_scratch.pdf")
self.layout.exportToPDF(scratch_output_pathname)
except Exception as e:
if not self.orm:
arcpy.AddError(f"Did you call ConfigureLayout? {e}")
else:
arcpy.AddError(f"Error in export; is file open (in Acrobat maybe)? {e}")
return False
projfile = os.path.join(self.project.homeFolder, self.project.filePath)
person = os.environ['USERNAME']
# Update the metadata on the PDF file. Then delete the scratch file.
try:
final_output_pathname = os.path.join(outputFolder, self.orm.filename + ".pdf")
writer=PdfWriter(clone_from=scratch_output_pathname)
metadata = {
'/Title': f"Taxmap {self.pageName}",
'/Subject': self.orm.longmaptitle + " Clatsop County, Oregon",
'/Author': f"{person} {self.layout.name}",
'/Creator': f'{projfile}', # Shows as "Application" in Acrobat
'/Producer': f"{__file__} {__version__}", # Shows as "PDF Producer"
'/Keywords': ','.join(['property tax', 'map', 'gis']),
}
writer.add_metadata(metadata)
writer.write(final_output_pathname)
arcpy.AddMessage(f"Exported to {final_output_pathname}")
except Exception as e:
arcpy.AddError(f"Error in export. {e}")
return
finally:
# Delete the file without the metadata
os.unlink(scratch_output_pathname)
return True
# =====================================================================================
if __name__ == "__main__":
DEFAULT_PROJECT_FILE = "taxmaps.aprx"
DEFAULT_CAN_TABLE = "W:\\ALL\\GIS\\CONNECTIONS\\cc-sqlservers_WINAUTH.sde\\cancelled_numbers"
assert(os.path.exists(DEFAULT_PROJECT_FILE))
assert(arcpy.Exists(DEFAULT_CAN_TABLE))
tm = BuildTaxMap(DEFAULT_PROJECT_FILE, "Taxmap_18x24", can_table=DEFAULT_CAN_TABLE)
pageNames = (tm.range(None, None))
print(f"There are {len(pageNames)} pages in the index.")
# pageNames = (tm.range('4 06', '4 09'))
# print(f"{len(pageNames)} pages will be exported.")
# This list of page names tests every aspect of the process.
sample_list = [
# scales
"4 06", # 1:24000 "2000 Scale"
"8 09", # 1:24000 "2000 Scale"
"5 10 19AD D2", # 1:120 "10 Scale"
"5 10 30AA D1", # 1:240 "20 Scale"
"6 10 3 D2", # 1:360 "30 Scale"
"6 10 16DD D3", # 1:480 "40 Scale"
"8 09 18BA D2", # 1:600 "50 Scale"
"6 10 4DA D4", # 1:720 "60 Scale"
"4 07 3BC", # 1:1200 "100 Scale"
"5 07 29A", # 1:2400 "200 Scale" SCALEBAR shows 0.1 Mile
"4 09 22", # 1:4800 "400 Scale" SCALEBAR shows 0.2 Mile
#"NONE", # 1:9600 "800 Scale"
#"NONE", # 1:12000 "1000 Scale"
]
print(f"{len(sample_list)} sample pages will be exported.")
for pageName in pageNames:
tm = BuildTaxMap(DEFAULT_PROJECT_FILE, "Taxmap_18x24", can_table=DEFAULT_CAN_TABLE)
print(f"Page {pageName}")
#tm.show_elements()
tm.ConfigureLayout(pageName)
scale = tm.ms.pageRow.MapScale
sbe = tm.find_scalebar(scale)
print(f"1:{scale} {sbe.longName}")
outputFolder = "C:\\Temp\\pdflib" # testing
tm.ExportPdf(outputFolder)
del tm # Attempt to release the project so APRX can be updated
# That's all!