diff --git a/tools/cm95_s52_style_compiler.py b/tools/cm95_s52_style_compiler.py index b3076e6..16a2b7d 100755 --- a/tools/cm95_s52_style_compiler.py +++ b/tools/cm95_s52_style_compiler.py @@ -238,6 +238,11 @@ def _scan_pbf_usage(path: Path) -> dict[str, Any]: if "s52_symbol_code" in props: if props.get("s52_sounding_symbol_sequence"): render_kind_counts["RUNTIME_SOUNDG"][placement] += 1 + elif props.get("s52_light_symbol_code") == props.get("s52_symbol_code"): + # LIGHTS05 materialization may intentionally mirror the flare into + # s52_symbol_code. Count/render that pure LIGHTS symbol only once + # through LIGHT_SY; beacon/tower body symbols remain separate. + pass else: render_kind_counts["SY"][placement] += 1 if "s52_light_symbol_code" in props: @@ -573,13 +578,28 @@ def _compile_style( "type": "symbol", "source": source_id, "source-layer": layer_name, - "filter": _base_filter( - priority=priority, - categories=categories, - required_field="s52_symbol_code", - supported_values=symbol_names, - forbidden_field="s52_sounding_symbol_sequence", - ), + "filter": [ + "all", + *_base_filter( + priority=priority, + categories=categories, + required_field="s52_symbol_code", + supported_values=symbol_names, + forbidden_field="s52_sounding_symbol_sequence", + )[1:], + [ + "!", + [ + "all", + ["has", "s52_light_symbol_code"], + [ + "==", + ["get", "s52_symbol_code"], + ["get", "s52_light_symbol_code"], + ], + ], + ], + ], "layout": { "icon-image": ["get", "s52_symbol_code"], "icon-anchor": "center", @@ -619,10 +639,10 @@ def _compile_style( "s52_light_symbol_code", light_symbol_offsets ), "icon-size": 1, - "icon-rotate": [ - "to-number", ["get", "s52_light_rotation_deg"], 0 - ], - "icon-rotation-alignment": "map", + # OpenCPN's raster sprite already contains the final flare + # orientation. s52_light_rotation_deg is retained in the + # PBF contract for vector/runtime backends, but applying it + # again here rotates the raster flare a second time. "icon-allow-overlap": True, "icon-ignore-placement": True, },