通过上文的内容,已经了解到S-57文件符合ISO/IEC 8211作为其数据封装标准。一个文件已分成若干个逻辑记录(LR),每一个LR有三个基本元素头标区(对应类S57Leader
)、目录区(对应类S57Directory
)和字段区,其中文件中的第一个LR为DDR(存储文件中实际数据的描述和逻辑结构,对应类S57DDR
),其他的LR为DR(存储文件中的实际数据,对应类S57DR
)。
依照上图,新建类Iso8211File
解析S-57文件,主要包含一个S57DDR
字段和一个List<S57DR>
列表。由于DDR和DR的头标区与目录区格式一致,因此将字段S57Leader
和字段S57Directory
放到类S57LR
中,让DDR和DR继承自LR。DDR的字段区包含字段控制字段和数据描述字段,因此类S57DDR
还包含字段S57FieldControlField
和字段S57DataDescriptiveFields
。DR的字段区解析逻辑与DDR不一样,因此类S57DR
还包含字段S57FieldArea
。最后得到如下代码结构:
public class S57LR
{
public S57Leader Leader; //头标区
public S57Directory Directory; //目录区
}
public class S57DDR : S57LR
{
public S57FieldControlField FieldControlField; //字段区 字段控制字段
public S57DataDescriptiveFields DataDescriptiveFields; //字段区 数据描述字段
}
public class S57DR : S57LR
{
public S57FieldArea FieldArea; //字段区 字段控制字段
}
public class Iso8211File
{
public S57DDR DDR;
public List<S57DR> DRs;
}
接下来,完成Iso8211File的构造函数,输入参数为S-57文件的路径。首先解析出文件的DDR头标区、目录录、字段控制字段和数据描述字段,利用其信息,循环解析出其他DR,具体代码如下:
//构造函数,传入S57文件路径
public Iso8211File(string filePath)
{
DDR = new S57DDR();
DRs = new List<S57DR>();
//加载S57文件,得到二进制数据
BytesHelper.Load(filePath);
//DDR 头标区
DDR.Leader = new S57Leader();
//DDR 目录区
DDR.Directory = new S57Directory(DDR.Leader);
//DDR 字段区
//DDR 字段控制字段
var tag0000 = DDR.Directory.Items[0];
DDR.FieldControlField = new S57FieldControlField(tag0000.FieldLength, DDR.Leader.FieldTagSize);
//DDR 数据描述字段
DDR.DataDescriptiveFields = new S57DataDescriptiveFields(DDR.Directory);
//循环解析DR
while (BytesHelper.Position < BytesHelper.ENCBytes.Length - 2)
{
var dr = new S57DR();
//DR 头标区
dr.Leader = new S57Leader();
//DR 目录区
dr.Directory = new S57Directory(dr.Leader);
//DR 字段区
dr.FieldArea = new S57FieldArea(dr.Directory, DDR.DataDescriptiveFields);
DRs.Add(dr);
}
}
最后验证结果:
var reader = new Iso8211File("..\\US4AK7IM.000");
var dl = reader.DDR.Leader;
var header = dl.RecordLength.ToString() + dl.FieldControlString
+ dl.FieldAreaBaseAddress.ToString() + dl.ExCharacterSetIndicator
+ dl.FieldLengthSize.ToString() + dl.FieldPositionSize.ToString()
+ dl.Reserved.ToString() + dl.FieldTagSize.ToString();
Console.WriteLine("DDR头标区:" + header);
Console.WriteLine("DDR目录区:");
foreach (var di in reader.DDR.Directory.Items)
{
Console.WriteLine($"标签:{di.Tag}\t长度:{di.FieldLength}\t位置:{di.FieldPosition}");
}
Console.WriteLine("DR的个数为:" + reader.DRs.Count);
至此,S-57文件的解析部分已全部完成了,我们可以清楚的掌握文件中的每一个标签,各标签下都有什么属性,各属性的值是什么。但不同标签对应真实世界实体的什么?不同的属性代表什么含义?各属性的值又能说明什么?需要对S-57标准的进一步解读。