 Look-up 表變成程序:拆解 S-101 的 215 條渲染規(guī)則)
上一篇拆 PC 分發(fā)件時(shí)留了個(gè)坑目錄里登記著 215 個(gè)規(guī)則文件的引用但解開(kāi)包只看到數(shù)據(jù)——符號(hào)怎么畫(huà)有 XML顏色怎么配有 colorProfile唯獨(dú)沒(méi)有什么條件下畫(huà)什么的邏輯。S-52 時(shí)代的 Look-up 表好歹是一張能讀的表S-101 把它變成了 215 個(gè) Lua 文件——210 個(gè)物標(biāo)規(guī)則外加 5 個(gè)PortrayalAPI、S100Scripting之類的框架與公共文件。這些文件不在圖示表達(dá)包里而是以 CSPConditional Symbology Procedure條件制圖程序源碼的形式隨產(chǎn)品規(guī)范發(fā)布——IHO 官網(wǎng)上各產(chǎn)品規(guī)范的 Portrayal 附件里就能下到。本文拆的這批對(duì)應(yīng)S-101 Ed 2.0.0 配套的圖示表達(dá)目錄 v2.0.0——215 個(gè) Lua 文件逐個(gè)實(shí)讀數(shù)目正好與目錄登記對(duì)上。這件事的分量值得單獨(dú)說(shuō)一句條件制圖從給人讀的文檔變成了給機(jī)器跑的程序。S-52 PresLib 時(shí)代PL 3.4 / PL 4.0 里的條件邏輯是偽代碼每家渲染器自己翻譯成 C——翻譯過(guò)程就是實(shí)現(xiàn)分歧的溫床。S-101 干脆把官方實(shí)現(xiàn)Lua連同文檔一起發(fā)你要做的不是照著文檔寫(xiě)而是把這段 Lua 嵌進(jìn)去跑。這篇挑三個(gè)代表拆開(kāi)最小的——錨泊船規(guī)則四十來(lái)行當(dāng)入門(mén)樣本最燒腦的——深度區(qū)和它的鄰居們管安全等深線怎么畫(huà)最后一族干脆不用 Lua——換 XSLT 引擎的規(guī)則。最小的規(guī)則錨泊船AnchorBerth.lua全文 43 行是理解 CSP 結(jié)構(gòu)的好樣本。核心判斷只有兩處local symbol ACHBRT07 -- default for categoryOfCargo ! 7 if contains(7, feature.categoryOfCargo) then symbol ANCBDNG2 end第一個(gè)條件就值得停下屬性直接改寫(xiě)符號(hào)。錨泊船裝的是危險(xiǎn)品categoryOfCargo 含 7符號(hào)從普通錨泊換成危險(xiǎn)品錨泊標(biāo)——船員掃一眼符號(hào)就知道這片錨地里混著危險(xiǎn)品船。這行代碼在 S-52 里對(duì)應(yīng) Look-up 表里的一條帶條件的行語(yǔ)義沒(méi)變只是載體從表行變成了 if。幾何類型分支是第二處if feature.PrimitiveType PrimitiveType.Point then ... elseif feature.PrimitiveType PrimitiveType.Surface then ... featurePortrayal:SimpleLineStyle(dash,0.64,CHMGF) featurePortrayal:AddInstructions(LineInstruction:_simple_)點(diǎn)要素給符號(hào)面要素除了符號(hào)還要畫(huà)邊界——邊界不引用外部線樣式文件而是用SimpleLineStyle內(nèi)聯(lián)一條簡(jiǎn)單的虛線0.64 寬、CHMGF 淺品紅。S-52 里這個(gè)區(qū)分寫(xiě)在 Look-up 表的兩個(gè)不同條目里現(xiàn)在變成了顯式的 if-else。還有一處容易被忽略但很關(guān)鍵if contextParameters.RadarOverlay then featurePortrayal:AddInstructions(ViewingGroup:26220;DrawingPriority:15;DisplayPlane:OverRadar) else featurePortrayal:AddInstructions(...DisplayPlane:UnderRadar)contextParameters是船員設(shè)置不是要素屬性。雷達(dá)疊加開(kāi)著錨泊符號(hào)畫(huà)在雷達(dá)圖像上層關(guān)著就畫(huà)在下層。同一個(gè)物標(biāo)畫(huà)面層級(jí)跟著用戶參數(shù)走——這就是為什么渲染器沒(méi)法把畫(huà)在哪層寫(xiě)死也是 CSP 簽名里帶contextParameters的原因。要素屬性、幾何類型、用戶上下文三股輸入在這里匯合。最后一個(gè)小細(xì)節(jié)要素帶名字時(shí)規(guī)則追加一條文本注記且內(nèi)容走模板——EncodeString(GetFeatureName(feature, contextParameters), Nr %s)。圖上Nr 7這樣的錨泊編號(hào)格式是規(guī)則里定的不是渲染器拼的。連一個(gè)前綴字符串的歸屬都被規(guī)范收走了。順帶看一眼指令協(xié)議本身AddInstructions吃一個(gè)分號(hào)分隔的字符串ViewingGroup:26220;DrawingPriority:15;DisplayPlane:UnderRadar——分組、優(yōu)先級(jí)、顯示面一條指令流。渲染器要做的只是實(shí)現(xiàn)這個(gè)指令集的解釋器規(guī)則怎么寫(xiě)它一概不關(guān)心。最燒腦的規(guī)則深度區(qū)和它的鄰居們DEPARE03.lua有 150 余行處理的是海圖上最要命的問(wèn)題安全等深線到底畫(huà)在哪。前文講過(guò)安全等深線是船員按吃水設(shè)的參數(shù)改一個(gè)數(shù)整張圖的填色分布重算。重算在 S-101 里的實(shí)體就是這段 Luaif depthRangeMinimumValue contextParameters.SafetyContour then unsafe true else safe true end水深小于安全等深線這片深度區(qū)標(biāo)記為 unsafe——但真正的重頭戲在后面。深度區(qū)畫(huà)邊界時(shí)要判斷我這條邊是不是安全等深線而判斷依據(jù)不是自己的屬性是鄰居是誰(shuí)for curveAssociation in feature:GetFlattenedSpatialAssociations() do local associatedFeatures curveAssociation.AssociatedFeatures ...DEPARE03 遍歷自己每條共享邊把共邊的要素?fù)瞥鰜?lái)分堆共邊的是DepthContour看它的深度值是不是恰好等于安全等深線loc_safety。是LandArea、UnsurveyedArea那要看對(duì)方的水線效果waterLevelEffect——陸地和未測(cè)量區(qū)貼著的邊按 unsafe 處理。連內(nèi)陸水域都有特判River、Lake、Canal、DockArea、LockBasin 算一組還要看線性建筑物的水線效果屬性。這段代碼里埋著一句真實(shí)工程味的注釋-- NOTE: S-52 PL 3.4 disagrees with PL 4.0.1 on the inlandWaterShared and watlev. -- Going with 3.4 since 4.0.1 doesnt apply the proper weight when safe water is -- next to a land area.S-52 預(yù)庫(kù) 3.4 版和 4.0.1 版在內(nèi)陸水域的判定上標(biāo)準(zhǔn)自己打架了官方實(shí)現(xiàn)選邊站 3.4理由寫(xiě)在注釋里。條件制圖程序化的一個(gè)意外好處就在這實(shí)現(xiàn)分歧沒(méi)法再藏在各家 C 里它被顯式地?cái)[在了代碼注釋中型式認(rèn)可時(shí)審的就是這一行。DEPARE03 里還有兩處值得圈出來(lái)。其一位置質(zhì)量直接映射到線型if qualityOfPosition and qualityOfPosition ~ 1 and qualityOfPosition ~ 10 and qualityOfPosition ~ 11 then featurePortrayal:SimpleLineStyle(dash,0.64,DEPSC)這條邊如果位置測(cè)量質(zhì)量不達(dá)標(biāo)qualityOfPosition 不在可信值列表里安全等深線畫(huà)成虛線——測(cè)得不準(zhǔn)的等深線在圖上就該長(zhǎng)得不確定。數(shù)據(jù)質(zhì)量參與渲染決策這是 S-100 系列相對(duì) S-57 最實(shí)質(zhì)的進(jìn)步之一而它就落在這幾行里。其二規(guī)則是可組合的。文件頭require RESCSP03、require SAFCON01、require SEABED01——管制區(qū)注記、安全水深標(biāo)注、海床顯示各是獨(dú)立的 CSP被 DEPARE03 按需調(diào)用。215 個(gè)規(guī)則文件不是 215 個(gè)孤立函數(shù)是一張調(diào)用網(wǎng)。連性能樁都標(biāo)準(zhǔn)化了Debug.StartPerformance(Lua Code - DEPARE03)官方實(shí)現(xiàn)自帶計(jì)時(shí)。順帶一提長(zhǎng)度上的冠軍另有其人——礙航物 OBSTRN07.lua 有 188 行沉船、適淹礁、水線效果的多層嵌套判成迷宮不過(guò)讀懂了深度區(qū)其它規(guī)則都是它的變奏。另一族不用 Lua 的 XSLTLua 不是唯一載體。S-111 表面流、S-123 海上無(wú)線電服務(wù)這批產(chǎn)品規(guī)范的圖示表達(dá)用的是XSLT——規(guī)則文件長(zhǎng)這樣S-123 的海警站42 行xsl:template matchCoastguardStation[primitivePoint] priority1 pointInstruction ... viewingGroup12310/viewingGroup displayPlaneOVERRADAR/displayPlane symbol referenceCostGuardStattion/ /pointInstruction xsl:if testcommunicationChannel! textInstruction.../textInstruction /xsl:if /xsl:transform對(duì)比 AnchorBerth 那段 Lua語(yǔ)義一一對(duì)應(yīng)模板匹配管幾何類型[primitivePoint]xsl:if管條件注記輸出的是指令 XML 而不是指令流字符串。一個(gè)是命令式Lua 主動(dòng) AddInstructions一個(gè)是聲明式XSLT 匹配后產(chǎn)出 XML條件制圖的語(yǔ)義是同一套。順帶一個(gè)彩蛋注意引用里那個(gè)CostGuardStattion——雙寫(xiě)的 t官方分發(fā)件原樣帶著這個(gè)拼寫(xiě)錯(cuò)誤。同一個(gè)包里還有拼錯(cuò)的Meterorological文件名少個(gè) o 的版本和正確版本并存——一個(gè) S-123 包貢獻(xiàn)兩處拼寫(xiě)彩蛋標(biāo)準(zhǔn)是工程產(chǎn)物這件事在細(xì)節(jié)里到處都是實(shí)錘。為什么兩套并存XSLT 是 S-52 預(yù)庫(kù)時(shí)代就確立的技術(shù)路線這批產(chǎn)品規(guī)范延續(xù)了它S-101 較新規(guī)則換成了 Lua。但血統(tǒng)是一致的——S-131 的線樣式文件頭里照樣寫(xiě)著sourceS52Preslib4.0下一篇細(xì)看這個(gè)文件PresLib 的符號(hào)資產(chǎn)在新框架里繼續(xù)服役。對(duì)我們做渲染器的人實(shí)際含義是解析器要么支持兩種規(guī)則引擎要么在接入不同產(chǎn)品規(guī)范前先做一層轉(zhuǎn)換。我們的做法是工具里雙引擎并列同一個(gè) S-131 分發(fā)包實(shí)測(cè)吃下 41 個(gè) Lua 規(guī)則S-123 分發(fā)包實(shí)測(cè)吃下 26 個(gè) XSLT 規(guī)則文件87 個(gè) XML、657 個(gè)符號(hào)覆蓋 11 個(gè)物標(biāo)兩條管道各自出渲染預(yù)覽。這件事的真正含義把邏輯標(biāo)準(zhǔn)化成可執(zhí)行程序改變的不只是寫(xiě)法渲染器退化成解釋器。指令集ViewingGroup / PointInstruction / LineInstruction / …成為唯一穩(wěn)定接口規(guī)則包整體可替換——IHO 發(fā)新版 PC換包不換碼。對(duì)照 S-52 時(shí)代每家把 Look-up 翻譯進(jìn) C升級(jí)一次預(yù)庫(kù)等于重新翻譯一次。實(shí)現(xiàn)分歧無(wú)處可藏。3.4 對(duì) 4.0.1 的取舍寫(xiě)在官方源碼注釋里誰(shuí)跟誰(shuí)不一致、跟了誰(shuí)全都可查。型式認(rèn)可審顯示對(duì)不對(duì)時(shí)審的不再是你按文檔理解對(duì)了嗎而是你跑的是不是這段代碼。測(cè)試有了錨點(diǎn)。S-64 的官方測(cè)試數(shù)據(jù)集為什么能當(dāng)標(biāo)準(zhǔn)答案前文說(shuō)過(guò)現(xiàn)在補(bǔ)上另一半——數(shù)據(jù)集喂進(jìn)來(lái)規(guī)則跑出去兩側(cè)都是標(biāo)準(zhǔn)件對(duì)不對(duì)得上逐條可比。規(guī)則文件、繪圖資源、顏色配置三樣湊齊渲染器才算拿到了完整的畫(huà)法。想親手翻這 215 個(gè)規(guī)則工具頁(yè)的 PC 解析器支持上傳整個(gè)分發(fā)包目錄每個(gè)物標(biāo)的渲染預(yù)覽、規(guī)則源碼、符號(hào)實(shí)圖都在里面S-101 和 S-123 的包都能直接吃。下一篇進(jìn) S-131港口基礎(chǔ)設(shè)施產(chǎn)品——S-100 的產(chǎn)品譜系從航海走向港口運(yùn)營(yíng)連系船柱和岸電設(shè)施都成了物標(biāo)。圖示表達(dá)規(guī)則換到新場(chǎng)景里怎么組織本篇雙引擎實(shí)測(cè)用的那個(gè) S-131 PC 2.0.0 分發(fā)包到時(shí)候正好登場(chǎng)。我是夜航海圖做海圖與地圖渲染開(kāi)發(fā)的工程師。博客「航圖筆記」同步更新全部文章歡迎 RSS 訂閱這個(gè)系列持續(xù)更新S-100 全家族拆解中關(guān)注博主不迷路完整在線工具與最新文章見(jiàn) nightchart.cn。