2.2.10 电子海图系统解析及开发 海图显示 - 符号、复杂线型、图案

S-52标准中,所用到的符号、复杂线型及填充图案,都用矢量符号描述符号记录在相应的数据文件中。数据文件的编排规则相同,每一行数据的前四个字符,表示字段的名称;第5到10个字符,表示字段的长度;第10个字符之后的内容,表示字段的内容。

1. 符号文件S52Symbols

[
SYMB   10SY02035NIL
SYMD   39ACHARE02V012670105200402005030106100789
SXPO   90anchorage area as a point at small scale, or anchor points of mooring trot at large scale�
SCRF    6ACHMGD
SVCT   32SPA;SW1;PU1264,789;PD1264,1291;�
SVCT   31SPA;SW1;PU1108,938;PD1412,938;�
SVCT   57SPA;SW1;PU1061,1188;PD1167,1292;PD1365,1292;PD1463,1191;�
],
// ......
[
SYMB   10SY02038NIL
SYMD   39WRECKS05V007500075000590004100045300540
SXPO   31dangerous wreck, depth unknown�
SCRF   12ADEPVSCCHBLK
SVCT  196SPA;SW1;ST0;PU453,700;PM0;PD512,625;PD600,575;PD687,550;PD800,540;PD900,575;PD981,625;PD1040,700;PD1040,800;PD987,859;PD900,915;PD800,950;PD700,950;PD600,915;PD525,865;PD453,800;PD453,700;PM2;FP;�
SVCT   29SPC;SW1;PU600,650;PD600,850;�
SVCT   29SPC;SW1;PU900,650;PD900,850;�
SVCT   30SPC;SW1;PU500,750;PD1000,750;�
SVCT   29SPC;SW1;PU750,600;PD750,900;�
SVCT   22SPC;SW1;PU453,700;PD;�
SVCT   22SPC;SW1;PU512,625;PD;�
SVCT   22SPC;SW1;PU600,575;PD;�
SVCT   22SPC;SW1;PU687,550;PD;�
SVCT   22SPC;SW1;PU800,540;PD;�
SVCT   22SPC;SW1;PU900,575;PD;�
SVCT   22SPC;SW1;PU981,625;PD;�
SVCT   23SPC;SW1;PU1040,700;PD;�
SVCT   22SPC;SW1;PU453,800;PD;�
SVCT   22SPC;SW1;PU525,865;PD;�
SVCT   22SPC;SW1;PU600,915;PD;�
SVCT   22SPC;SW1;PU700,950;PD;�
SVCT   22SPC;SW1;PU800,950;PD;�
SVCT   22SPC;SW1;PU900,915;PD;�
SVCT   22SPC;SW1;PU987,859;PD;�
SVCT   23SPC;SW1;PU1043,793;PD;�
]

上面包含两个符号的相关说明,每个符号由如下字段构成(以首个符号为例):

  • SYMB
    • 2个字符 'SY' 表示符号
    • 5个字符 02035 表示符号ID
    • 3个字符 NIL=新版;ADD=新增;MOD=替换;DEL=删除
  • SYMD
    • 8个字符 'ACHARE02' 表示字符名
    • 1个字符 符号类型 'V'=矢量;‘R’=光栅
    • 5个字符 01267=转心横坐标
    • 5个字符 01052=转心纵坐标
    • 5个字符 00402=绘制区宽度
    • 5个字符 00503=绘制区高度
    • 5个字符 01061=绘制区起点横坐标
    • 5个字符 00789=绘制区起点纵坐标
  • SXPO
    • 符号的描述及说明
  • SCRF
    • 颜色引用表(长度为6的倍数,首字母为缩写,后5个字符为颜色标记):A=CHMGD
  • SVCT
    • 具体的符号化指令(可分多行)

在类S52Tools里,添加如下常量:

    //一英尺 = 96像素 = 25.4 毫米, 每毫米像素数
    public const double MPP = 96 / 25.4;
    //画布上每单位像素数,一单位 = 001mm
    public const float UPP = (float)MPP * 0.01f;

新建类S52Symbol存储符号的相关信息:

    public class S52Symbol
    {
        public int RCID;                    //标识符
        public string Name;                 //名称
        public string Description;          //描述
        public int PivotPointX;             //转心坐标
        public int PivotPointY; 
        public int BoundingBoxWidth;        //绘制区宽度
        public int BoundingBoxHeight;       //绘制区高度
        public int BoundingBoxX;            //绘制区起点坐标
        public int BoundingBoxY;
        public Dictionary<char, SKColor> Colors = new Dictionary<char, SKColor>(); //颜色
        public List<string> Instructions = new List<string>();   //绘图指令

        //偏移量
        public int OffsetX => (int)((PivotPointX - BoundingBoxX) * S52Tools.UPP);
        public int OffsetY => (int)((PivotPointY - BoundingBoxY) * S52Tools.UPP);

        private SKBitmap img;
        public SKBitmap Image              //绘制后的符号
        {
            get
            {
                if(img == null)
                {
                    //图片大小
                    var width = (int)(BoundingBoxWidth * S52Tools.UPP) + 2;
                    var height = (int)(BoundingBoxHeight * S52Tools.UPP) + 2;

                    img = new SKBitmap(width, height);
                    using (var ca = new SKCanvas(img))
                    {
                        ca.Clear();
                        ca.Scale(S52Tools.UPP, S52Tools.UPP);  //缩放
                        ca.Translate(-BoundingBoxX + 13, -BoundingBoxY + 13); //平移

                        //使用矢量符号描述语言绘制
                        S52Tools.DrawSymbol(ca, Colors, Instructions);
                    }
                }

                return img;
            }
        }
    }

新建符号目录,将符号文件添加进项目S57Viewer,新建单例模式的类S52Symbols,对外提供符号名称的索引器,以快速获取相应符号。如:获取缩写为"ACHARE02"的符号S52Symbols.Instance["ACHARE02"]

    //符号目录
    public sealed class S52Symbols
    {
        private static readonly S52Symbols instance = new S52Symbols();

        //显示的static 构造函数
        static S52Symbols() { }

        private S52Symbols() { }

        public static S52Symbols Instance => instance;

        Dictionary<string, S52Symbol> nameObjs = new Dictionary<string, S52Symbol>();

        public S52Symbol this[string acronym]
        {
            get
            {
                LoadResource();
                return nameObjs[acronym];
            }
        }

        private void LoadResource()
        {
            if (nameObjs.Count > 0) return;  //首次才需要加载资源文件

            var lines = Encoding.ASCII.GetString(Properties.Resources.S52Symbols).Split('\n');

            S52Symbol obj = null;
            for (int i = 0; i < lines.Length; i++)
            {
                if (string.IsNullOrWhiteSpace(lines[i])) continue;
                if (lines[i][0] == '[')  //开始
                {
                    obj = new S52Symbol();
                    continue;
                }
                if (lines[i][0] == ']') //结束
                {
                    nameObjs.Add(obj.Name, obj);
                    continue;
                }

                var line = lines[i];
                var tag = line.Substring(0, 4);

                if (tag == "SYMB") obj.RCID = int.Parse(line.Substring(11, 5));
                else if (tag == "SYMD")
                {
                    obj.Name = line.Substring(9, 8);
                    obj.PivotPointX = int.Parse(line.Substring(18, 5));
                    obj.PivotPointY = int.Parse(line.Substring(23, 5));
                    obj.BoundingBoxWidth = int.Parse(line.Substring(28, 5));
                    obj.BoundingBoxHeight = int.Parse(line.Substring(33, 5));
                    obj.BoundingBoxX = int.Parse(line.Substring(38, 5));
                    obj.BoundingBoxY = int.Parse(line.Substring(43, 5));
                }
                else if (tag == "SXPO") obj.Description = line.Substring(9);
                else if (tag == "SCRF")
                {
                    for (int j = 9; j <= line.Length - 6; j += 6)
                    {
                        obj.Colors.Add(line[j], S52Colors.Instance[line.Substring(j + 1, 5)]);
                    }
                }
                else if (tag == "SVCT") obj.Instructions.Add(line.Substring(9));
            }
        }
    }

经测试,符号文件解析及绘制正常:

符号示例

2. 复杂线型文件S52ComplexLines

[
LNST   10LS03346NIL
LIND   38ACHARE51001080082003030005030030600568
LXPO   30boundary of an anchorage area�
LCRF    6ACHMGD
LVCT   32SPA;SW1;PU1429,568;PD1429,1070;�
LVCT   31SPA;SW1;PU1273,717;PD1577,717;�
LVCT   55SPA;SW1;PU1226,967;PD1332,1071;PD1530,1071;PD1628,970;�
LVCT   29SPA;SW1;PU306,815;PD906,815;�
LVCT   31SPA;SW1;PU2732,815;PD3336,815;�
LVCT   42SPA;SW1;PU2868,813;PD3030,975;PD3199,807;�
LVCT   31SPA;SW1;PU1928,814;PD2528,814;�
LVCT   42SPA;SW1;PU2068,812;PD2233,976;PD2397,812;�
LVCT   39SPA;SW1;PU390,812;PD546,974;PD713,813;�
],
//......
[
LNST   10LS02034NIL
LIND   38UNITMTR1002760064401109004470027600644
LXPO   49change of depth unit line, bounds 'metre depths'�
LCRF    6ACHGRD
LVCT   30SPA;SW1;PU276,644;PD1385,644;�
LVCT   61SPA;SW1;PU701,782;PD701,1089;PD804,786;PD908,1091;PD908,786;�
]

上面包含两个符号的相关说明,每个符号由如下字段构成(以首个符号为例):

  • LNST
    • 2个字符 'LS' 表示复杂线型
    • 5个字符 03346 表示符号ID
    • 3个字符 NIL=新版;ADD=新增;MOD=替换;DEL=删除
  • LIND
    • 8个字符 'ACHARE51' 表示字符名
    • 5个字符 00108=转心横坐标
    • 5个字符 00820=转心纵坐标
    • 5个字符 03030=绘制区宽度
    • 5个字符 00503=绘制区高度
    • 5个字符 00306=绘制区起点横坐标
    • 5个字符 00568=绘制区起点纵坐标
  • LXPO
    • 复杂线型的描述及说明
  • LCRF
    • 颜色引用表(长度为6的倍数,首字母为缩写,后5个字符为颜色标记):A=CHMGD
  • LVCT
    • 具体的符号化指令(可分多行)

新建复杂线型目录,将复杂线型文件添加进项目S57Viewer,新建单例模式的类S52ComplexLines,对外提供符号名称的索引器,以快速获取相应符号。如:获取缩写为"ACHARE51"的复杂线型S52ComplexLines.Instance["ACHARE51"]

    //复杂线型目录
    public sealed class S52ComplexLines
    {
        private static readonly S52ComplexLines instance = new S52ComplexLines();

        //显示的static 构造函数
        static S52ComplexLines() { }

        private S52ComplexLines() { }

        public static S52ComplexLines Instance => instance;

        Dictionary<string, S52Symbol> nameObjs = new Dictionary<string, S52Symbol>();

        public S52Symbol this[string acronym]
        {
            get
            {
                LoadResource();
                return nameObjs[acronym];
            }
        }

        private void LoadResource()
        {
            if (nameObjs.Count > 0) return;  //首次才需要加载资源文件

            var lines = Encoding.ASCII.GetString(Properties.Resources.S52ComplexLines).Split('\n');

            S52Symbol obj = null;
            for (int i = 0; i < lines.Length; i++)
            {
                if (string.IsNullOrWhiteSpace(lines[i])) continue;
                if (lines[i][0] == '[')  //开始
                {
                    obj = new S52Symbol();
                    continue;
                }
                if (lines[i][0] == ']') //结束
                {
                    nameObjs.Add(obj.Name, obj);
                    continue;
                }

                var line = lines[i];
                var tag = line.Substring(0, 4);

                if (tag == "LNST") obj.RCID = int.Parse(line.Substring(11, 5));
                else if (tag == "LIND")
                {
                    obj.Name = line.Substring(9, 8);
                    obj.PivotPointX = int.Parse(line.Substring(17, 5));
                    obj.PivotPointY = int.Parse(line.Substring(22, 5));
                    obj.BoundingBoxWidth = int.Parse(line.Substring(27, 5));
                    obj.BoundingBoxHeight = int.Parse(line.Substring(32, 5));
                    obj.BoundingBoxX = int.Parse(line.Substring(37, 5));
                    obj.BoundingBoxY = int.Parse(line.Substring(42, 5));
                }
                else if (tag == "LXPO") obj.Description = line.Substring(9);
                else if (tag == "LCRF")
                {
                    for (int j = 9; j <= line.Length - 6; j += 6)
                    {
                        obj.Colors.Add(line[j], S52Colors.Instance[line.Substring(j + 1, 5)]);
                    }
                }
                else if (tag == "LVCT") obj.Instructions.Add(line.Substring(9));
            }
        }
    }

经测试,复杂线型文件解析及绘制正常:

复杂线型中的基本元素

3. 图案文件S52Patterns

[
PATT   10PT02000NIL
PATD   55AIRARE02VSTGCON0200010000022590225600618005280043500452
PXPO   39pattern of symbols for an airport area�
PCRF    6ALANDF
PVCT  150SPA;SW1;PU623,980;PD859,980;PD790,901;PD790,801;PD1053,801;PD810,638;PD810,516;PD751,452;PD680,516;PD680,638;PD435,795;PD684,797;PD684,907;PD623,980;�
],
//......
[
PATT   10PT03001NIL
PATD   55VEGATN04VSTGCON0100010000007500075000400003000055000450
PXPO   33pattern of symbols for mangroves�
PCRF    6ALANDF
PVCT   25SPA;SW1;PU750,600;CI150;�
PVCT   29SPA;SW1;PU550,750;PD950,750;�
PVCT   29SPA;SW1;PU750,748;PD750,587;�
]

上面包含两个符号的相关说明,每个符号由如下字段构成(以首个符号为例):

  • PATT
    • 2个字符 'PT' 表示图案
    • 5个字符 02000 表示符号ID
    • 3个字符 NIL=新版;ADD=新增;MOD=替换;DEL=删除
  • LIND
    • 8个字符 'AIRARE02' 表示字符名
    • 1个字符 符号类型 'V'=矢量;‘R’=光栅
    • 3个字符 填充方式 'STG'=交错式;‘LIN’=线性式
    • 3个字符 填充间距 'CON'=固定;‘SCL’=随比例尺而变化
    • 5个字符 02000=图案间最小距离(包含转心与绘制区)
    • 5个字符 10000=图案间最大距离(固定间距时,此值无意义)
    • 5个字符 00108=转心横坐标
    • 5个字符 00820=转心纵坐标
    • 5个字符 03030=绘制区宽度
    • 5个字符 00503=绘制区高度
    • 5个字符 00306=绘制区起点横坐标
    • 5个字符 00568=绘制区起点纵坐标
  • PXPO
    • 复杂线型的描述及说明
  • PCRF
    • 颜色引用表(长度为6的倍数,首字母为缩写,后5个字符为颜色标记):A=CHMGD
  • PVCT
    • 具体的符号化指令(可分多行)

相比于符号和复杂线型,图案新增了填充方式、填充间距、图案间最大/最小距离。图案文件中,填充间距都是固定的,因此图案间最大距离没有意义,图案间最小距离其实就是各图案在填充时的实际距离。新建类S52Pattern继承至S52Symbol

    public class S52Pattern: S52Symbol
    {
        public string TypeOfFillPattern;        //填充方式
        public int MinimumDistance;             //图案最小距离
    }

新建图案目录,将图案文件添加进项目S57Viewer,新建单例模式的类S52Patterns,对外提供符号名称的索引器,以快速获取相应符号。如:获取缩写为"AIRARE02"的图案S52Patterns.Instance["AIRARE02"]

    //图案填充目录
    public sealed class S52Patterns
    {
        private static readonly S52Patterns instance = new S52Patterns();

        //显示的static 构造函数
        static S52Patterns() { }

        private S52Patterns() { }

        public static S52Patterns Instance => instance;

        Dictionary<string, S52Pattern> nameObjs = new Dictionary<string, S52Pattern>();

        public S52Pattern this[string acronym]
        {
            get
            {
                LoadResource();
                return nameObjs[acronym];
            }
        }

        private void LoadResource()
        {
            if (nameObjs.Count > 0) return;  //首次才需要加载资源文件

            var lines = Encoding.ASCII.GetString(Properties.Resources.S52Patterns).Split('\n');

            S52Pattern obj = null;
            for (int i = 0; i < lines.Length; i++)
            {
                if (string.IsNullOrWhiteSpace(lines[i])) continue;
                if (lines[i][0] == '[')  //开始
                {
                    obj = new S52Pattern();
                    continue;
                }
                if (lines[i][0] == ']') //结束
                {
                    nameObjs.Add(obj.Name, obj);
                    continue;
                }

                var line = lines[i];
                var tag = line.Substring(0, 4);

                if (tag == "PATT") obj.RCID = int.Parse(line.Substring(11, 5));
                else if (tag == "PATD")
                {
                    obj.Name = line.Substring(9, 8);
                    obj.TypeOfFillPattern = line.Substring(18, 3);
                    obj.MinimumDistance = int.Parse(line.Substring(24, 5));
                    obj.PivotPointX = int.Parse(line.Substring(34, 5));
                    obj.PivotPointY = int.Parse(line.Substring(39, 5));
                    obj.BoundingBoxWidth = int.Parse(line.Substring(44, 5));
                    obj.BoundingBoxHeight = int.Parse(line.Substring(49, 5));
                    obj.BoundingBoxX = int.Parse(line.Substring(54, 5));
                    obj.BoundingBoxY = int.Parse(line.Substring(59, 5));
                }
                else if (tag == "PXPO") obj.Description = line.Substring(9);
                else if (tag == "PCRF")
                {
                    for (int j = 9; j <= line.Length - 6; j += 6)
                    {
                        obj.Colors.Add(line[j], S52Colors.Instance[line.Substring(j + 1, 5)]);
                    }
                }
                else if (tag == "PVCT") obj.Instructions.Add(line.Substring(9));
            }
        }
    }

经测试,复杂线型文件解析及绘制正常:

面区域填充中的基本图案
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容