Add outline color field; headings can be regrouped now

This commit is contained in:
Fuhrmann 2024-12-06 09:57:31 +01:00
parent dee854e5c1
commit 1582317c59

View file

@ -2,6 +2,8 @@
import arcpy # type: ignore import arcpy # type: ignore
DEFAULT_LINE_WIDTH = 0.7
class Toolbox: class Toolbox:
def __init__(self): def __init__(self):
@ -45,7 +47,7 @@ class FeatureRenderer:
table_param = arcpy.Parameter( table_param = arcpy.Parameter(
displayName="Select Legend Table", displayName="Select Legend Table",
name="table", name="table",
datatype="DETable", datatype="GPTableView",
parameterType="Required", parameterType="Required",
direction="Input", direction="Input",
) )
@ -74,9 +76,9 @@ class FeatureRenderer:
field_param_2.parameterDependencies = [table_param.name] field_param_2.parameterDependencies = [table_param.name]
# Define parameter for selecting a stroke symbol code field # Define parameter for selecting a line symbol code field
field_param_3 = arcpy.Parameter( field_param_3 = arcpy.Parameter(
displayName="Select Stroke Symbol Code Field", displayName="Select Line Symbol Code Field",
name="line_symbol", name="line_symbol",
datatype="Field", datatype="Field",
parameterType="Required", parameterType="Required",
@ -169,18 +171,31 @@ class FeatureRenderer:
heading_field.parameterDependencies = [table_param.name] heading_field.parameterDependencies = [table_param.name]
# Define Boolean parameter # Define parameter to choose to draw outlines
draw_outlines = arcpy.Parameter( draw_outlines = arcpy.Parameter(
displayName="Draw outlines", displayName="Draw outlines",
name="draw_outlines", name="draw_outlines",
datatype="GPBoolean", datatype="GPBoolean",
parameterType="Required", parameterType="Required",
direction="Input", direction="Input",
category="Define Outlines",
) )
# Default value # Default value
draw_outlines.value = True draw_outlines.value = True
# Define parameter for outline field
outline_field = arcpy.Parameter(
name="outline_field",
displayName="Select Outline Field",
datatype="Field",
direction="Input",
parameterType="Optional",
category="Define Outlines",
)
outline_field.parameterDependencies = [table_param.name]
# Return parameter definitions as a list # Return parameter definitions as a list
return [ return [
style_files, style_files,
@ -195,6 +210,7 @@ class FeatureRenderer:
label_field_2, label_field_2,
heading_field, heading_field,
draw_outlines, draw_outlines,
outline_field,
] ]
def isLicensed(self): def isLicensed(self):
@ -223,6 +239,7 @@ class FeatureRenderer:
label_field_2 = parameters[9].valueAsText label_field_2 = parameters[9].valueAsText
heading_field = parameters[10].valueAsText heading_field = parameters[10].valueAsText
draw_outlines = parameters[11].value draw_outlines = parameters[11].value
outline_field = parameters[12].valueAsText
# Retrieve currently active map # Retrieve currently active map
active_map = project.activeMap active_map = project.activeMap
@ -296,9 +313,10 @@ class FeatureRenderer:
for row in search_cursor: for row in search_cursor:
custom_labels[row[0]] = f"{row[1]}{label_delimieter}{row[2]}" custom_labels[row[0]] = f"{row[1]}{label_delimieter}{row[2]}"
# Retrieve headings
headings = {}
if heading_field: if heading_field:
labels = [primary_key_field, heading_field] labels = [primary_key_field, heading_field]
headings = {}
with arcpy.da.SearchCursor( with arcpy.da.SearchCursor(
table, table,
labels, labels,
@ -306,6 +324,16 @@ class FeatureRenderer:
for row in search_cursor: for row in search_cursor:
headings[str(row[0])] = row[1] headings[str(row[0])] = row[1]
# Retrieve outline color codes
outline_codes = {}
if outline_field:
with arcpy.da.SearchCursor(
table,
[primary_key_field, outline_field],
) as search_cursor:
for row in search_cursor:
outline_codes[str(row[0])] = row[1]
# Start rendering process # Start rendering process
for layer in map_layers: for layer in map_layers:
if layer.name in layers_to_render: if layer.name in layers_to_render:
@ -317,42 +345,6 @@ class FeatureRenderer:
# Check if UniqueValueRenderer is defined # Check if UniqueValueRenderer is defined
if sym.renderer == "UniqueValueRenderer": if sym.renderer == "UniqueValueRenderer":
# Regroup items according to heading field
if heading_field:
new_item_groups = {}
old_item_groups = {}
for group in sym.renderer.groups:
old_item_groups[group.heading] = group.items
for group in sym.renderer.groups:
for item in group.items:
if item.values[0][0] != "<Null>":
leg_id = str(item.values[0][0])
else:
leg_id = None
if leg_id in headings:
heading = headings[leg_id]
if not heading:
heading = "Other"
else:
heading = "Other"
if heading in new_item_groups:
new_item_groups[heading].append(item)
else:
new_item_groups[heading] = [item]
# Remove old item groups
sym.renderer.removeValues(old_item_groups)
layer.symbology = sym
# Add new item groups
sym.renderer.addValues(new_item_groups)
layer.symbology = sym
# Apply symbols from gallery # Apply symbols from gallery
for group in sym.renderer.groups: for group in sym.renderer.groups:
for item in group.items: for item in group.items:
@ -376,11 +368,19 @@ class FeatureRenderer:
symbols_from_gallery = ( symbols_from_gallery = (
item.symbol.listSymbolsFromGallery(symbol_key) item.symbol.listSymbolsFromGallery(symbol_key)
) )
symbol_found = False
for symbol_from_gallery in symbols_from_gallery: for symbol_from_gallery in symbols_from_gallery:
if symbol_from_gallery.name == symbol_key: if symbol_from_gallery.name == symbol_key:
symbol_found = True
item.symbol = symbol_from_gallery item.symbol = symbol_from_gallery
break break
# Print message if symbol could not be found
if not symbol_found:
messages.AddMessage(
f"Could not find symbol in gallery {symbol_key}"
)
# Add user defined labels # Add user defined labels
if add_labels: if add_labels:
if leg_id in custom_labels: if leg_id in custom_labels:
@ -392,39 +392,98 @@ class FeatureRenderer:
cim_lyr = layer.getDefinition("V3") cim_lyr = layer.getDefinition("V3")
# Retrieve stroke symbol properties # Retrieve stroke symbol properties
stroke_symbol_props = {"color": {}, "width": 0} stroke_symbol_props = {}
if draw_outlines: for group in cim_lyr.renderer.groups:
for group in cim_lyr.renderer.groups: for unique_value_class in group.classes:
for unique_value_class in group.classes: if unique_value_class.values[0].fieldValues[0] != "<Null>":
for ( leg_id = str(
symbol_layer unique_value_class.values[0].fieldValues[0]
) in unique_value_class.symbol.symbol.symbolLayers: )
if isinstance( else:
symbol_layer, leg_id = None
arcpy.cim.CIMSymbols.CIMSolidStroke,
): stroke_symbol_props[leg_id] = {
stroke_symbol_props["color"] = ( "color": None,
symbol_layer.color "width": None,
}
for (
symbol_layer
) in unique_value_class.symbol.symbol.symbolLayers:
if isinstance(
symbol_layer,
arcpy.cim.CIMSymbols.CIMSolidStroke,
):
if leg_id in outline_codes:
# Set user defined outline color
outline_code = outline_codes[leg_id]
color_value = get_symbol_color(outline_code)
color = arcpy.cim.CreateCIMObjectFromClassName(
"CIMCMYKColor", "V3"
) )
stroke_symbol_props["width"] = ( color.values = color_value["CMYK"]
symbol_layer.width stroke_symbol_props[leg_id]["color"] = color
) else:
break if symbol_layer.color:
# Set color as it was before
stroke_symbol_props[leg_id][
"color"
] = symbol_layer.color
else:
# Set default color
color = (
arcpy.cim.CreateCIMObjectFromClassName(
"CIMCMYKColor", "V3"
)
)
color.values = [40, 40, 40, 10, 100]
stroke_symbol_props[leg_id]["color"] = color
if stroke_symbol_props["color"]: if draw_outlines:
break if symbol_layer.width:
# Set width as it was before
stroke_symbol_props[leg_id][
"width"
] = symbol_layer.width
else:
# Set default width
stroke_symbol_props[leg_id][
"width"
] = DEFAULT_LINE_WIDTH
else:
stroke_symbol_props[leg_id]["width"] = 0
# Set default stroke width and color if unset break
if draw_outlines and stroke_symbol_props["width"] == 0:
stroke_symbol_props["width"] = 0.7
if not stroke_symbol_props["color"]: # In case the layer did not have a stroke symbol layer or outline color
color = arcpy.cim.CreateCIMObjectFromClassName( if not stroke_symbol_props[leg_id]["color"]:
"CIMCMYKColor", "V3" if leg_id in outline_codes:
) messages.AddMessage("Add color")
color.values = [40, 40, 40, 10, 100] # Set user defined outline color
stroke_symbol_props["color"] = color outline_code = outline_codes[leg_id]
color_value = get_symbol_color(outline_code)
color = arcpy.cim.CreateCIMObjectFromClassName(
"CIMCMYKColor", "V3"
)
color.values = color_value["CMYK"]
stroke_symbol_props[leg_id]["color"] = color
else:
# Set default color
color = arcpy.cim.CreateCIMObjectFromClassName(
"CIMCMYKColor", "V3"
)
color.values = [40, 40, 40, 10, 100]
stroke_symbol_props[leg_id]["color"] = color
if not stroke_symbol_props[leg_id]["width"]:
if draw_outlines:
stroke_symbol_props[leg_id][
"width"
] = DEFAULT_LINE_WIDTH
else:
stroke_symbol_props[leg_id]["width"] = 0
new_groups = []
for group in cim_lyr.renderer.groups: for group in cim_lyr.renderer.groups:
for unique_value_class in group.classes: for unique_value_class in group.classes:
@ -471,10 +530,10 @@ class FeatureRenderer:
arcpy.cim.CIMSymbols.CIMSolidStroke, arcpy.cim.CIMSymbols.CIMSolidStroke,
): ):
symbol_layer.width = ( symbol_layer.width = (
stroke_symbol_props["width"] stroke_symbol_props[leg_id]["width"]
) )
symbol_layer.color = ( symbol_layer.color = (
stroke_symbol_props["color"] stroke_symbol_props[leg_id]["color"]
) )
elif layer_shape_type == "Polyline": elif layer_shape_type == "Polyline":
@ -495,7 +554,7 @@ class FeatureRenderer:
# Draw symbol background colors # Draw symbol background colors
if not has_color and color_value: if not has_color and color_value:
if layer_shape_type == "Polygon": if layer_shape_type == "Polygon":
# Add fill and stroke symbol layers # Add fill symbol layer
fill_symbol = arcpy.cim.CIMSolidFill() fill_symbol = arcpy.cim.CIMSolidFill()
update_color(fill_symbol, color_value) update_color(fill_symbol, color_value)
@ -504,17 +563,14 @@ class FeatureRenderer:
) )
# Add stroke symbol layer # Add stroke symbol layer
if ( if stroke_symbol_props[leg_id]:
draw_outlines
and stroke_symbol_props["color"]
):
stroke_symbol = arcpy.cim.CIMSolidStroke() stroke_symbol = arcpy.cim.CIMSolidStroke()
stroke_symbol.color = stroke_symbol_props[ stroke_symbol.color = stroke_symbol_props[
"color" leg_id
] ]["color"]
stroke_symbol.width = stroke_symbol_props[ stroke_symbol.width = stroke_symbol_props[
"width" leg_id
] ]["width"]
unique_value_class.symbol.symbol.symbolLayers.append( unique_value_class.symbol.symbol.symbolLayers.append(
stroke_symbol stroke_symbol
) )
@ -533,6 +589,36 @@ class FeatureRenderer:
marker_symbol marker_symbol
) )
# Regroup items
if heading_field:
if leg_id in headings:
heading = headings[leg_id]
if not heading:
heading = "Other"
else:
heading = "Other"
matched_group = next(
(
group
for group in new_groups
if group.heading == heading
),
None,
)
if matched_group:
matched_group.classes.append(unique_value_class)
else:
new_group = arcpy.cim.CreateCIMObjectFromClassName(
"CIMUniqueValueGroup", "V3"
)
new_group.heading = heading
new_group.classes.append(unique_value_class)
new_groups.append(new_group)
if heading_field:
cim_lyr.renderer.groups = new_groups
# Push changes back to layer object # Push changes back to layer object
layer.setDefinition(cim_lyr) layer.setDefinition(cim_lyr)
@ -633,7 +719,10 @@ def get_percentage_from_letter(letter):
elif letter == "X": elif letter == "X":
return 0 return 0
else: else:
return int(letter) * 10 try:
return int(letter) * 10
except ValueError as _:
raise ValueError(f"Execution aborted: unknown color code {letter}")
def get_symbol_color(color_string): def get_symbol_color(color_string):