fix: restore LIGHTS CA web overlay

This commit is contained in:
OpenAI Codex
2026-09-21 17:13:27 +08:00
parent 79b8d6cd5b
commit 30f6bacef2
2 changed files with 27 additions and 8 deletions

View File

@@ -424,9 +424,13 @@ function numericProp(props,key,fallback=0){
function lightArcPoint(feature){
const geom=feature && feature.geometry;
if(!geom)return null;
if(geom.type==="Point")return geom.coordinates;
if(geom.type==="MultiPoint" && geom.coordinates && geom.coordinates.length)return geom.coordinates[0];
return null;
let coord=null;
if(geom.type==="Point")coord=geom.coordinates;
else if(geom.type==="MultiPoint" && geom.coordinates && geom.coordinates.length)coord=geom.coordinates[0];
if(!Array.isArray(coord) || coord.length<2)return null;
const lng=Number(coord[0]),lat=Number(coord[1]);
if(!Number.isFinite(lng) || !Number.isFinite(lat))return null;
return [lng,lat];
}
function refreshLightArcFeatures(){
@@ -551,7 +555,9 @@ function drawLightArcFeature(ctx,feature,bearing,width,height){
const props=feature.properties || {};
const coord=lightArcPoint(feature);
if(!coord)return;
const point=leftMap.project(coord);
let point=null;
try{point=leftMap.project(coord);}catch(err){console.warn("light arc project failed",feature.id,coord,err);return;}
if(!point || !Number.isFinite(point.x) || !Number.isFinite(point.y))return;
const args=CM95S52Runtime.jsonValue(props.runtime_args_json,[]);
const metrics=caScaleMetrics(props,coord,width,height);
const radius=metrics.radius;
@@ -575,10 +581,14 @@ function drawLightArcFeature(ctx,feature,bearing,width,height){
function drawLightArcs(){
lightArcDrawPending=false;
const prepared=prepareLightArcCanvas();
if(!leftMap || !leftMap.getSource("cm95") || !leftMap.isStyleLoaded())return;
if(!leftMap || !leftMap.getSource("cm95") || !leftMap.isSourceLoaded("cm95"))return;
const bearing=leftMap.getBearing();
for(const feature of lightArcFeatures){
drawLightArcFeature(prepared.ctx,feature,bearing,prepared.width,prepared.height);
try{
drawLightArcFeature(prepared.ctx,feature,bearing,prepared.width,prepared.height);
}catch(err){
console.warn("light arc draw skipped",feature && feature.id,err);
}
}
}
function scheduleLightArcDraw(){
@@ -838,7 +848,16 @@ function inspect(map,lngLat,element){
element.textContent="地图仍在加载。";
return;
}
const point=map.project(lngLat);
let point=null;
try{point=map.project(lngLat);}catch(err){
console.warn("inspector project failed",lngLat,err);
element.textContent="投影坐标不可用。";
return;
}
if(!point || !Number.isFinite(point.x) || !Number.isFinite(point.y)){
element.textContent="投影坐标不可用。";
return;
}
const box=[[point.x-5,point.y-5],[point.x+5,point.y+5]];
let features=[];
try{features=map.queryRenderedFeatures(box);}catch(err){console.warn(err);}

View File

@@ -112,7 +112,7 @@ class RuntimeExecutor{
addSequence(seq){const base={source:this.sourceId};this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-ac`,type:"fill",filter:this.filter(seq,"AC"),paint:{"fill-color":["get","runtime_color"],"fill-opacity":0.92}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-ap`,type:"fill",filter:this.filter(seq,"AP"),paint:{"fill-pattern":["get","runtime_resource"],"fill-opacity":0.85}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-ls`,type:"line",filter:this.filter(seq,"LS"),layout:{"line-cap":"round"},paint:{"line-color":["get","runtime_color"],"line-width":["get","runtime_line_width"],"line-dasharray":["match",["get","runtime_line_style"],"DASH",["literal",[4,2]],"DOTT",["literal",[1,2]],["literal",[1,0]]]}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-lc`,type:"line",filter:this.filter(seq,"LC"),paint:{"line-pattern":["get","runtime_resource"],"line-width":5}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-sy`,type:"symbol",filter:this.filter(seq,"SY"),layout:{"icon-image":["get","runtime_resource"],"icon-offset":["coalesce",["get","runtime_icon_offset"],["literal",[0,0]]],"icon-allow-overlap":true,"icon-ignore-placement":true}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-soundg`,type:"symbol",filter:this.filter(seq,"SOUNDG"),layout:{"icon-image":["get","runtime_resource"],"icon-offset":["coalesce",["get","runtime_icon_offset"],["literal",[0,0]]],"icon-allow-overlap":true,"icon-ignore-placement":true}}));this.addLayer(Object.assign({},base,{id:`runtime-rule-${seq}-tx`,type:"symbol",filter:this.filter(seq,"TX"),layout:{"text-field":["get","runtime_text"],"text-size":10,"text-offset":["literal",[0,0]],"text-allow-overlap":false,"text-optional":true},paint:{"text-color":["get","runtime_color"],"text-halo-color":"rgba(255,255,255,.65)","text-halo-width":0.5}}));}
sourceLayers(){const set=new Set();((this.map.getStyle()||{}).layers||[]).forEach(layer=>{if(layer.source==="cm95"&&layer["source-layer"])set.add(layer["source-layer"]);});return Array.from(set);}
updateSafety(){const values=[];try{(this.map.querySourceFeatures("cm95",{sourceLayer:"depth_contour"})||[]).forEach(f=>{const v=numberValue(f.properties,"contour_depth_m",null);if(v!==null&&v>=this.settings.safetyContourM)values.push(v);});}catch(_err){}this.effectiveSafetyContourM=values.length?Math.min.apply(null,values):null;}
refresh(){if(!this.map.getSource("cm95")||!this.map.isSourceLoaded("cm95"))return;this.updateSafety();this.lightLabelState.reset();const seen=new Set(),features=[],context={effectiveSafetyContourM:this.effectiveSafetyContourM,lightLabelState:this.lightLabelState,symbolMetadata:this.symbolMetadata};this.sourceLayers().forEach(layer=>{let rows=[];try{rows=this.map.querySourceFeatures("cm95",{sourceLayer:layer})||[];}catch(_err){return;}rows.forEach(feature=>{if(!feature.properties)return;const presentations=jsonValue(feature.properties.s52_light_presentations,[]),variants=[];if(feature.properties.s52_rule_chain)variants.push(feature);if(Array.isArray(presentations))presentations.forEach((presentation,index)=>variants.push(Object.assign({},feature,{id:String(feature.id==null?"":feature.id)+":light:"+index,properties:Object.assign({},feature.properties,presentation)})));variants.forEach(variant=>{if(!variant.properties.s52_rule_chain)return;const key=[layer,variant.id,JSON.stringify(variant.geometry),variant.properties.s52_rule_chain].join("|");if(seen.has(key))return;seen.add(key);operationFeatures(variant,this.settings,context,name=>this.map.hasImage(name)).forEach(item=>features.push(item));});});});this.caFeatures=features.filter(item=>item.properties.runtime_opcode==="CA");const source=this.map.getSource(this.sourceId);if(source&&source.setData)source.setData({type:"FeatureCollection",features:features});}
refresh(){if(!this.map.getSource("cm95")||!this.map.isSourceLoaded("cm95"))return;this.updateSafety();this.lightLabelState.reset();const seen=new Set(),features=[],context={effectiveSafetyContourM:this.effectiveSafetyContourM,lightLabelState:this.lightLabelState,symbolMetadata:this.symbolMetadata};this.sourceLayers().forEach(layer=>{let rows=[];try{rows=this.map.querySourceFeatures("cm95",{sourceLayer:layer})||[];}catch(_err){return;}rows.forEach(feature=>{if(!feature.properties)return;const presentations=jsonValue(feature.properties.s52_light_presentations,[]),variants=[];if(feature.properties.s52_rule_chain)variants.push(feature);if(Array.isArray(presentations))presentations.forEach((presentation,index)=>variants.push(Object.assign({},feature,{type:"Feature",id:String(feature.id==null?"":feature.id)+":light:"+index,geometry:feature.geometry,properties:Object.assign({},feature.properties,presentation)})));variants.forEach(variant=>{if(!variant.properties.s52_rule_chain)return;const key=[layer,variant.id,JSON.stringify(variant.geometry),variant.properties.s52_rule_chain].join("|");if(seen.has(key))return;seen.add(key);operationFeatures(variant,this.settings,context,name=>this.map.hasImage(name)).forEach(item=>features.push(item));});});});this.caFeatures=features.filter(item=>item.properties.runtime_opcode==="CA");const source=this.map.getSource(this.sourceId);if(source&&source.setData)source.setData({type:"FeatureCollection",features:features});}
}
const api={DEFAULTS:DEFAULTS,COLORS:COLORS,jsonValue:jsonValue,soundingRules:soundingRules,hazardRules:hazardRules,dynamicRules:dynamicRules,expandRuleChain:expandRuleChain,operationFeatures:operationFeatures,caScaleMetrics:caScaleMetrics,LightLabelState:LightLabelState,RuntimeExecutor:RuntimeExecutor};root.CM95S52Runtime=api;if(typeof module!=="undefined"&&module.exports)module.exports=api;
})(typeof globalThis!=="undefined"?globalThis:this);