通過矢量符號描述語言,可以知道符號具體形狀;通過符號化指令,可以對指定的位置對點、線、面物標進行繪制;那落實到S-57數(shù)據(jù)中的一條條記錄而言,具體調用哪些符號化指令,這就需要用到符號指令查詢表(Look-Up Tables)。S-57物標目錄中的每一類物標,都可以通過查詢表獲取其符號化指令。
若對于某物標獲取不到其符號化指令,缺省值為:NODTA顏色填充,NODATA03圖案填充,顯示級別為0,被雷達圖像覆蓋,基本顯示,組別為11050。
查詢表結構
查詢文件的編排規(guī)則為每一行數(shù)據(jù)的前四個字符,表示字段的名稱;第5到10個字符,表示字段的長度;第10個字符之后的內容,表示字段的內容。
[
LUPT 29LU00491NILCONVYRL00008OLINES?
ATTC 16CATCON1?CONRAD3?
INST 83LS(DASH,4,CHGRD);SY(RACNSP01);TE(clr {0:0.0},VERCLR,3,1,2,15110,1,0,CHBLK,11)?
DISC 12DISPLAYBASE?
LUCM 612210?
],
// ......
[
LUPT 40LU00009NILACHAREA00003SPLAIN_BOUNDARIES?
ATTC 8CATACH8?
INST 43SY(ACHARE02);LS(DASH,2,CHMGF);CS(RESTRN01)?
DISC 9STANDARD?
LUCM 626220?
],
上面包含兩條記錄的相關說明,每個符號指令由如下字段構成(以首條記錄為例):
- LUPT
- 2個字符 'LU' 表示查詢表
- 5個字符 00491 表示標識符
- 3個字符 NIL=新版;ADD=新增;MOD=替換;DEL=刪除
- 6個字符 物標目錄類別 ‘CONVYR’(傳送帶)
- 1個字符 物標幾何類別 'A'=面物標;'L'=線物標;'P'=點物標
- 5個字符 顯示優(yōu)先級 00008
- 1個字符 雷達優(yōu)先級 'O'=在雷達圖像上;'S'=在雷達圖像下
- 最后其他字符 顯示設置
?面物標可以是:'PLAIN_BOUNDARIES'(簡單邊界)或 'SYMBOLIZED_ BOUNDARIES'(符號化邊界)
?點物標可以是:'SIMPLIFIED'(簡單符號)或 'PAPER_CHART'(紙質海圖符號)
?線物標只能是:'LINES'簡單邊界是用實線、虛線、點線去給區(qū)域描邊;而符號化邊界是用復雜線型填充區(qū)域邊界。
簡單符號是用簡單的幾何圖形加顏色當作燈標、沉船等點物標的符號;而紙質海圖符號則是用與紙質海圖一樣的符號標注點物標。
- ATTC 屬性組合(可重復,也可空)
- 6個字符 表示屬性
- 1~15個字符 表示屬性性
不同屬性之間由單元分隔符隔開,屬性值可為空,也可用'?'表示。
示例中存在兩組屬性值:CATCON=1和CONRAD=3
- INST
- 符號指令 示例中為:用虛線描繪,線中點設置符號RACNSP01,同時顯示格式文本
- DISC
- 顯示模式 ‘DISPLAYBASE’=基礎顯示;?‘STANDARD’=標準顯示;?‘OTHER’=其他顯示
- LUCM
- 描述與說明
綜上可知:對于線物標CONVYR(傳送帶),如果其包含屬性值CATCON=1(傳送帶類型=帶式)和CONRAD=3(雷達顯著物標=安裝雷達反射器),那么用虛線描邊,線段中點設置符號RACNSP01,同時顯示格式文本VERCLR(凈空高度)。
讀取查詢表
仿照上述查詢表記錄結構,新建類S52LookUp。
//查詢表 單條記錄
public class S52LookUp
{
//LUPT
public int RCID; //標識符
public UInt16 ObjectClassCode; //物標目錄類別
public char ObjectType; //幾何類別 'A' Area 'L' Line 'P' Point
public int Priority; //優(yōu)先級
public char RaderPriority; //雷達優(yōu)先級 'O'=在雷達圖像上;'S'=在雷達圖像下
//PLAIN_BOUNDARIES;SYMBOLIZED_BOUDARIES (Areas)
//SIMPLIFIED; PAPER_CHART (Points)
//LINES (Lines)
public string DisplaySet; //顯示設置
//ATTC
public UInt16[] AttrCodes;
public string[] AttrValues;
//INST
public string[][] Instruction; //符號指令 拆分成二維數(shù)組
//DISC
public string DisplayCategory; //顯示模式
//LUCM
public string ViewingGroup; //組號
//缺省值
public static S52LookUp Default
{
get
{
return new S52LookUp()
{
Priority = 0,
RaderPriority = 'S',
DisplayCategory = "DISPLAYBASE",
ViewingGroup = "11050",
Instructions = new string[][]
{
new string[] { "AC", "NODTA" },
new string[] { "AP", "PRTSUR01" }
}
};
}
}
}
將查詢表文件S52LookupTable添加進項目S57Parser,新建單例模式的類S52LookUps存儲整個查詢表,讓其繼承至List<S52LookUp>:
public class S52LookUps : List<S52LookUp>
{
private static readonly S52LookUps instance = new S52LookUps();
//顯示的static 構造函數(shù)
static S52LookUps() { }
private S52LookUps()
{
var lines = Encoding.ASCII.GetString(Properties.Resources.S52LookupTable).Split('\n');
S52LookUp obj = null;
for (int i = 0; i < lines.Length; i++)
{
if (string.IsNullOrWhiteSpace(lines[i])) continue;
if (lines[i][0] == '[') //開始
{
obj = new S52LookUp();
continue;
}
if (lines[i][0] == ']') //結束
{
this.Add(obj);
continue;
}
var line = lines[i];
var tag = line.Substring(0, 4);
var lastUtIndex = line.LastIndexOf(Helper.UT);
if (tag == "LUPT")
{
obj.RCID = int.Parse(line.Substring(11, 5));
obj.ObjectClassCode = S57Objects.Instance[line.Substring(19, 6)].Code;
obj.ObjectType = line[25];
obj.Priority = int.Parse(line.Substring(26, 5));
obj.RaderPriority = line[31];
obj.DisplaySet = line.Substring(32, lastUtIndex-32);
}
else if (tag == "ATTC")
{
var attc = line.Substring(9, lastUtIndex - 9);
if (string.IsNullOrWhiteSpace(attc)) continue;
var ats = attc.Split(Helper.UT);
obj.AttrCodes = new UInt16[ats.Length];
obj.AttrValues = new string[ats.Length];
for (int j = 0; j < ats.Length; j++)
{
obj.AttrCodes[j] = S57Attributes.Instance[ats[j].Substring(0, 6)].Code;
obj.AttrValues[j] = ats[j].Substring(6);
}
}
else if (tag == "INST")
{
string[] inst = line.Substring(9, lastUtIndex - 9).Split(';');
obj.Instructions = new string[inst.Length][];
for (int j = 0; j < inst.Length; j++)
{
obj.Instructions[j] = inst[j].Split('(', ',', ')');
}
}
else if (tag == "DISC") obj.DisplayCategory = line.Substring(9, lastUtIndex - 9);
else if (tag == "LUCM") obj.ViewingGroup = line.Substring(9, lastUtIndex - 9);
}
}
public static S52LookUps Instance => instance;
}
使用查詢表
查詢表中規(guī)定了物標的顯示優(yōu)先級、顯示指令,是否被雷達圖像覆蓋等。海圖繪制時,先遍歷各物標,通過物標類別、幾何類型,用戶顯示設置(對于面物標和點物標有兩個顯示方式)為查找條件,找到查詢表匹配的記錄。若匹配記錄為0,則使用缺省指令;若匹配記錄為1,則直接使用該指令;若匹配記錄大于1,則需要利用屬性值進一步篩選。
利用屬性篩選的規(guī)則:
- 物標需包含查詢表記錄中所含有的屬性,并且值和順序也需要一樣。如:查詢表中屬性值4,3,4與物標中對應屬性值3,4,3或4,3是不匹配的,但與4,3,4,7一致,因為7不需要被符號化。
- 如果查詢表中屬性值缺失,則表明任何屬性值(除了unknown)都匹配。
- 如果查詢表中屬性值為‘?’,則只有物標屬性值=unknown才匹配。

編碼實現(xiàn)
新增用戶設置,記錄面物標是顯示符號化邊界還是簡單邊界,點物標是顯示簡單符號還是紙質海圖符號。
public static class MySettings
{
// ...
public static bool SYMBOLIZED_BOUDARIES = true; //顯示符號化邊界
public static bool PAPER_CHART = true; //顯示紙質海圖符號
public static string DisplayMode = "STANDARD"; //海圖模式
}
修改類S57Feature,添加字段public S52LookUp LookUp及比例尺范圍。
public class S57Feature : S57Record
{
// ......
public S52LookUp LookUp;
public int ScaleMinium => GetAttrValue(133, 0);
public int ScaleMaximum => GetAttrValue(132, 0);
}
工具類中S52Tools中,新增靜態(tài)方法S52LookUp FindLookUpEntry(S57Feature f),為特征記錄設置符號指令:
public static S52LookUp FindLookUpEntry(S57Feature f)
{
//幾何類型
var objType = f.PRIM == 1 ? 'P' : f.PRIM == 2 ? 'L' : 'A';
//物標類別+顯示模式
var entries = S52LookUps.Instance.Where(x => x.ObjectClassCode == f.OBJL
&& x.ObjectType == objType && x.DisplayCategory == MySettings.DisplayMode);
if (entries.Count() == 0) return S52LookUp.Default;
if (entries.Count() == 1) return entries.First();
if (f.PRIM == 1)
{
entries = entries.Where(x => x.DisplaySet == (MySettings.PAPER_CHART ? "PAPER_CHART" : "SIMPLIFIED"));
}
else if (f.PRIM == 3)
{
entries = entries.Where(x => x.DisplaySet == (MySettings.SYMBOLIZED_BOUDARIES ? "SYMBOLIZED_BOUDARIES" : "PLAIN_BOUNDARIES"));
}
if (entries.Count() == 0) return S52LookUp.Default;
if (entries.Count() == 1 || f.AttrCodes == null) return entries.First();
var lookup = entries.First(); //默認第一項
foreach (var en in entries)
{
if (en.AttrCodes == null) continue;
var isOk = true;
for (int i = 0; i < en.AttrCodes.Length; i++)
{
var index = -1;
for (var m = 0; m < f.AttrCodes.Length; m++)
{
if (f.AttrCodes[m] == en.AttrCodes[i])
{
index = m;
break;
}
}
if (index >= 0)
{
if (en.AttrValues[i] == "" && f.AttrValues[index] == "") //缺失
{
isOk = false;
break;
}
if (en.AttrValues[i] == "?" && f.AttrValues[index] != "")
{
isOk = false;
break;
}
//屬性的值不匹配
if (!f.AttrValues[index].StartsWith(en.AttrValues[i]))
{
isOk = false;
break;
}
}
else
{
//沒找到對應的屬性
isOk = false;
break;
}
}
if (isOk)
{
lookup = en;
break;
}
}
return lookup;
}