diff --git a/config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json b/config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json
index 53caea8..cd19b7d 100644
--- a/config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json
+++ b/config/s52/cm95_s52_runtime_capabilities_web_compare_v1.json
@@ -19,27 +19,28 @@
]
},
"RUNTIME_DEPARE": {
- "status": "PREVIEW_ONLY",
- "fidelity": "DEFAULT_MARINER_SETTINGS_PREVIEW",
+ "status": "IMPLEMENTED",
+ "fidelity": "ORDERED_RUNTIME_RULE_IR",
"evidence": [
{
- "path": "web/kyushu_compare.html",
+ "path": "web/s52_runtime_executor.js",
"contains": [
- "runtime-preview-depare",
- "runtimeDepthColorExpression"
+ "RuntimeExecutor",
+ "dynamicRules"
]
}
]
},
"RUNTIME_DEPCNT": {
- "status": "PREVIEW_ONLY",
- "fidelity": "DEFAULT_MARINER_SETTINGS_PREVIEW",
+ "status": "IMPLEMENTED",
+ "fidelity": "SAFE_LOOKUP_QUAPOS_AND_SIDE_EFFECTS",
"evidence": [
{
- "path": "web/kyushu_compare.html",
+ "path": "web/s52_runtime_executor.js",
"contains": [
- "runtime-preview-depcnt",
- "updateRuntimeSafetyContour"
+ "RuntimeExecutor",
+ "s52_safe_dashed_rule_chain",
+ "scamin:100000001"
]
}
]
@@ -49,22 +50,22 @@
"fidelity": "TEXT_VALUE_ONLY_NOT_S52_GLYPH_COMPOSITION",
"evidence": [
{
- "path": "web/kyushu_compare.html",
+ "path": "web/s52_runtime_executor.js",
"contains": [
- "runtime-preview-soundg"
+ "soundingRules"
]
}
]
},
"RUNTIME_OBSTRN": {
- "status": "PREVIEW_ONLY",
- "fidelity": "POINT_SYMBOL_RUNTIME_ONLY_AREA_LINE_PENDING",
+ "status": "IMPLEMENTED",
+ "fidelity": "ORDERED_POINT_LINE_AREA_RULE_CHAIN",
"evidence": [
{
- "path": "web/kyushu_compare.html",
+ "path": "web/s52_runtime_executor.js",
"contains": [
- "runtime-preview-hazard-symbols",
- "runtimeHazardSymbol",
+ "hazardRules",
+ "operationFeatures",
"OBSTRN04",
"ISODGR51"
]
@@ -72,14 +73,14 @@
]
},
"RUNTIME_WRECKS": {
- "status": "PREVIEW_ONLY",
- "fidelity": "POINT_SYMBOL_RUNTIME_ONLY_AREA_LINE_PENDING",
+ "status": "IMPLEMENTED",
+ "fidelity": "ORDERED_POINT_LINE_AREA_RULE_CHAIN",
"evidence": [
{
- "path": "web/kyushu_compare.html",
+ "path": "web/s52_runtime_executor.js",
"contains": [
- "runtime-preview-hazard-symbols",
- "runtimeHazardSymbol",
+ "hazardRules",
+ "operationFeatures",
"WRECKS02"
]
}
diff --git a/src/cm95pbf/csp.py b/src/cm95pbf/csp.py
index e73093b..a35bac2 100644
--- a/src/cm95pbf/csp.py
+++ b/src/cm95pbf/csp.py
@@ -620,6 +620,10 @@ def materialize_mariner_runtime_facts(
)
if name in {"OBSTRN04", "WRECKS02"}:
+ valsou = _optional_float(attrs.get("VALSOU"))
+ out["s52_valsou_present"] = int(valsou is not None)
+ if valsou is not None:
+ out["s52_valsou_m"] = valsou
hazard_depth = _runtime_hazard_depth(
name, object_class, geometry_type, attrs, associated_depth_ranges
)
@@ -634,6 +638,17 @@ def materialize_mariner_runtime_facts(
out["s52_surrounding_max_drval1_m"] = max(lows)
if highs:
out["s52_surrounding_min_drval2_m"] = min(highs)
+ quality = resolve_quapnt01_instruction(attrs)
+ out["s52_hazard_quality_rule_chain"] = materialize_rule_ir(
+ quality, attrs, source="nested_csp", procedure="QUAPNT01"
+ )
+ if name == "OBSTRN04" and attrs.get("OBJNAM") not in (None, ""):
+ out["s52_hazard_name_rule_chain"] = materialize_rule_ir(
+ "TX(OBJNAM,1,2,3,'15118',-1,-1,CHBLK,26)",
+ attrs,
+ source="nested_csp",
+ procedure="OBJNAM",
+ )
scalar_codes = {
"QUAPOS": "s52_quapos_code",
diff --git a/src/cm95pbf/mapping.py b/src/cm95pbf/mapping.py
index 2fd682a..812ab16 100644
--- a/src/cm95pbf/mapping.py
+++ b/src/cm95pbf/mapping.py
@@ -56,6 +56,10 @@ DELIVERY_PROPERTY_ALLOWLIST = frozenset(
"s52_display_base_promoted",
"s52_hazard_depth_m",
"s52_hazard_procedure",
+ "s52_hazard_quality_rule_chain",
+ "s52_hazard_name_rule_chain",
+ "s52_valsou_present",
+ "s52_valsou_m",
"s52_isolated_danger",
"s52_surrounding_max_drval1_m",
"s52_surrounding_min_drval2_m",
diff --git a/tools/cm95_s52_style_compiler.py b/tools/cm95_s52_style_compiler.py
index 97bdee5..6897027 100755
--- a/tools/cm95_s52_style_compiler.py
+++ b/tools/cm95_s52_style_compiler.py
@@ -170,6 +170,8 @@ USAGE_VALUE_FIELDS = (
"s52_area_color",
"s52_area_pattern_code",
"s52_line_color",
+ "s52_line1_color",
+ "s52_line2_color",
"s52_line_complex_code",
"s52_text1_color",
"s52_text2_color",
@@ -184,6 +186,10 @@ USAGE_PRESENCE_FIELDS = (
"s52_text1_value",
"s52_text2_value",
"s52_text3_value",
+ "s52_line1_style",
+ "s52_line1_width",
+ "s52_line2_style",
+ "s52_line2_width",
)
@@ -245,9 +251,13 @@ def _scan_pbf_usage(path: Path) -> dict[str, Any]:
render_kind_counts["AP"][placement] += 1
else:
geometry_ignored[("AP", layer_name, geometry_type)] += 1
- if "s52_line_color" in props:
+ line_slots = [
+ slot for slot in (1, 2)
+ if f"s52_line{slot}_color" in props
+ ]
+ if line_slots or "s52_line_color" in props:
if line_geometry:
- render_kind_counts["LS"][placement] += 1
+ render_kind_counts["LS"][placement] += max(1, len(line_slots))
else:
geometry_ignored[("LS", layer_name, geometry_type)] += 1
if "s52_line_complex_code" in props:
@@ -540,7 +550,7 @@ def _compile_style(
materialized_priority_fields = {
"AC": ("s52_area_color",),
"AP": ("s52_area_pattern_code",),
- "LS": ("s52_line_color",),
+ "LS": ("s52_line_color", "s52_line1_color", "s52_line2_color"),
"LC": ("s52_line_complex_code",),
"SY": ("s52_symbol_code",),
"TX/TE": ("s52_text1_value", "s52_text2_value", "s52_text3_value"),
@@ -578,6 +588,8 @@ def _compile_style(
for field in (
"s52_area_color",
"s52_line_color",
+ "s52_line1_color",
+ "s52_line2_color",
"s52_text1_color",
"s52_text2_color",
"s52_text3_color",
@@ -690,42 +702,77 @@ def _compile_style(
)
compiled_placements["AP"].add((layer_name, priority))
- if (
- "line" in caps
- and priority in opcode_priorities["LS"]
- and _field_used(usage, layer_name, "s52_line_color", priority)
- ):
- _add_layer(
- layers,
- compiled_counts,
- "LS",
- {
- "id": f"{prefix}-ls-{layer_id}",
- "type": "line",
- "source": source_id,
- "source-layer": layer_name,
- "filter": _base_filter(
- priority=priority,
- categories=categories,
- required_field="s52_line_color",
- ),
- "paint": {
- "line-color": color_line,
- "line-width": [
- "to-number",
- ["get", "s52_line_width"],
- float(config.get("line_width_fallback", 1.0)),
- ],
- "line-dasharray": [
- "case",
- ["==", ["get", "s52_line_style"], "DASH"],
- ["literal", [4, 2]],
- ["literal", [1, 0]],
- ],
+ if "line" in caps and priority in opcode_priorities["LS"]:
+ for slot in (1, 2):
+ color_field = f"s52_line{slot}_color"
+ if not _field_used(usage, layer_name, color_field, priority):
+ continue
+ style_field = f"s52_line{slot}_style"
+ width_field = f"s52_line{slot}_width"
+ _add_layer(
+ layers,
+ compiled_counts,
+ "LS",
+ {
+ "id": f"{prefix}-ls{slot}-{layer_id}",
+ "type": "line",
+ "source": source_id,
+ "source-layer": layer_name,
+ "filter": _base_filter(
+ priority=priority,
+ categories=categories,
+ required_field=color_field,
+ ),
+ "layout": {"line-cap": "round"},
+ "paint": {
+ "line-color": _color_expression(
+ color_field, active_colors, fallback
+ ),
+ "line-width": [
+ "to-number", ["get", width_field],
+ float(config.get("line_width_fallback", 1.0)),
+ ],
+ "line-dasharray": [
+ "match", ["get", style_field],
+ "DASH", ["literal", [4, 2]],
+ "DOTT", ["literal", [1, 2]],
+ ["literal", [1, 0]],
+ ],
+ },
},
- },
- )
- compiled_placements["LS"].add((layer_name, priority))
+ )
+ compiled_placements["LS"].add((layer_name, priority))
+
+ if _field_used(usage, layer_name, "s52_line_color", priority):
+ _add_layer(
+ layers,
+ compiled_counts,
+ "LS",
+ {
+ "id": f"{prefix}-ls-legacy-{layer_id}",
+ "type": "line",
+ "source": source_id,
+ "source-layer": layer_name,
+ "filter": _base_filter(
+ priority=priority,
+ categories=categories,
+ required_field="s52_line_color",
+ forbidden_field="s52_line1_color",
+ ),
+ "layout": {"line-cap": "round"},
+ "paint": {
+ "line-color": color_line,
+ "line-width": ["to-number", ["get", "s52_line_width"], 1],
+ "line-dasharray": [
+ "match", ["get", "s52_line_style"],
+ "DASH", ["literal", [4, 2]],
+ "DOTT", ["literal", [1, 2]],
+ ["literal", [1, 0]],
+ ],
+ },
+ },
+ )
+ compiled_placements["LS"].add((layer_name, priority))
if (
"line" in caps
and lc_mapping
@@ -1172,6 +1219,8 @@ def _compile_style(
for field in (
"s52_area_color",
"s52_line_color",
+ "s52_line1_color",
+ "s52_line2_color",
"s52_text1_color",
"s52_text2_color",
"s52_text3_color",
diff --git a/web/kyushu_compare.html b/web/kyushu_compare.html
index 6f2d269..a86c03e 100644
--- a/web/kyushu_compare.html
+++ b/web/kyushu_compare.html
@@ -76,6 +76,7 @@
+