2.2.9 電子海圖系統(tǒng)解析及開發(fā) 海圖顯示 - 矢量符號描述語言

符號

不同用途的燈標(biāo)、錨地邊界、岸上建筑物等物標(biāo)在海圖都有不同的符號,

符號尺度及轉(zhuǎn)心示意圖

符號所在畫布大小為32767個(gè)單位(每一單位代表0.01毫米)絮姆。

  • 每個(gè)符號有一個(gè)全局唯一的名稱,也擁有一套坐標(biāo)系秩霍,坐標(biāo)原點(diǎn)位于左上角篙悯。
  • 符號繪制的起點(diǎn)為(BoundingBoxX, BoundingBoxY),繪制區(qū)擁有一定高度(BoundingBoxHeight)與寬度(BoundingBoxWidth)铃绒。
  • 符號還存在一個(gè)轉(zhuǎn)心(PivotPointX, PivotPointY)鸽照,但轉(zhuǎn)心不一定位于繪制區(qū)內(nèi)部。在指定坐標(biāo)處繪制符號時(shí)匿垄,應(yīng)該將轉(zhuǎn)心位于指定坐標(biāo)處移宅;如果需要旋轉(zhuǎn)符號归粉,則旋轉(zhuǎn)的中心也為轉(zhuǎn)心。
  • 符號的具體形狀由一系列指令(Instructions漏峰,由矢量符號描述語言編寫)構(gòu)成糠悼。

矢量符號描述語言

S-52標(biāo)準(zhǔn)中,是用矢量符號描述語言(Vector Symbol Description Language)來定義這些符號形狀的浅乔。這些符號被廣泛用于電子海圖中定義點(diǎn)倔喂、復(fù)雜的線型及填充復(fù)雜的圖案。

矢量符號描述語言使用一支虛構(gòu)的“筆”靖苇,上繪畫席噩,然后記錄畫筆移動的位置,最終形式電子海圖所需的矢量符號贤壁。與屏幕坐標(biāo)一樣悼枢,畫布坐標(biāo)的原點(diǎn)(位置0,0)在圖形的左上角,x坐標(biāo)向右延伸脾拆,y坐標(biāo)向下延伸馒索。矢量符號描述語言畫筆的顏色、大小名船、移動等都是通過指令實(shí)現(xiàn)的绰上,其中:

  • ; ??不同的指令用分號隔開
  • , ??如果指令含有多個(gè)參數(shù),參數(shù)之間用逗號分隔渠驼;如果沒有參數(shù)蜈块,則無需添加逗號
  • SP ?顏色
    ???表示畫筆的顏色,該顏色用一個(gè)字母代表某種顏色標(biāo)記迷扇。SP指令中畫筆顏色對作用于其之后的指令百揭,直到新的畫筆出現(xiàn)為止。
  • ST ?透明度
    ???表示當(dāng)前顏色存在一定比例的透明度谋梭。一共四級(0~3)信峻,1級代表25%的透明度。
  • SW ?畫筆寬度
    ???規(guī)定畫筆寬度為幾個(gè)單位瓮床,每一個(gè)單位代表0.3毫米。
  • PU ?x坐標(biāo), y坐標(biāo) [,x, y, ... x, y]
    ???指的是畫筆移到坐標(biāo)(x, y)處产镐,此時(shí)并沒有繪制隘庄。
  • PD ?x坐標(biāo), y坐標(biāo) [,x, y, ... x, y]
    ???指的是畫筆從當(dāng)前位置畫到坐標(biāo)(x, y)處,繪制完畢后癣亚,畫筆位置會更新丑掺,畫筆顏色及大小由之前指令確定。
  • CI ?半徑
    ???繪制指定半徑大小的圓述雾,圓心為當(dāng)前位置街州,繪制完畢后兼丰,畫筆位置不會更新。
  • AA ?x坐標(biāo), y坐標(biāo), 角度
    ???繪制圓弧唆缴,參數(shù)中的坐標(biāo)(x, y)為弧線圓心的位置鳍征,弧線的起點(diǎn)為畫筆當(dāng)前位置。當(dāng)角度為正時(shí)面徽,表示逆時(shí)針旋轉(zhuǎn)艳丛,否則為順時(shí)針旋轉(zhuǎn)指定的角度數(shù)。繪制完畢后趟紊,畫筆位置會更新到圓弧終點(diǎn)氮双。
  • PM ?n
    ???多邊形模式。用于存儲將要繪制的多邊形霎匈,直到多邊形被完全定義完戴差。例如,要定義多邊形铛嘱,需要將筆移到所需位置造挽,然后執(zhí)行PM 0進(jìn)入多邊形模式,然后指定適當(dāng)?shù)闹噶钜远x多邊形的形狀弄痹。如果還要定義子多邊形饭入,以PM 1指令結(jié)束形狀并定義下一個(gè)形狀,直到執(zhí)行PM 2退出多邊形模式肛真。
  • EP ?
    ???繪制之前存儲的多邊形的邊界谐丢。
  • FP ?
    ???填充之前存儲的多邊形。
  • SC ?符號名, 方向
    ???在指定的方向上繪制另一個(gè)符號蚓让,符號的轉(zhuǎn)心(Pivot point)位于當(dāng)前畫筆位置乾忱。 如果方向=0,表示符號維持原方向历极;方向=1窄瘟,表示方向?yàn)樽詈螽嫻P繪制的方向;方向=2趟卸,表示符號沿符號化方向旋轉(zhuǎn)90度蹄葱。

示例

SPA;SW1;PU1000,1000;PD1000,2000;

畫筆顏色'A',寬度為1個(gè)單位锄列,移動到(1000, 1000)图云,繪制垂直線段到(1000, 2000)。

SPB;SW2;PU1000,1000;PD1000,2000,2000,2000,2000,1000,1000,1000;

畫筆顏色'B'邻邮,寬度為2個(gè)單位竣况,移動到(1000, 1000),繪制線段到(1000, 2000)筒严,接著一直繪制到(2000, 2000)丹泉,(2000, 1000)情萤,(1000, 1000),最終形成一個(gè)矩形摹恨。

SPB;ST2;PM0;PU1000,1000;PD1000,2000,2000,2000,2000,1000;PM2;FP;

進(jìn)入多邊形模式(PM0)筋岛,繪制上個(gè)示例中的矩形(多邊形最后自動首尾相連),最后退出多邊形模式(PM2)睬塌,用畫筆顏色'B'泉蝌,50%的透明度,填充多邊形揩晴。

PU100,100;PM0;CI50;PM2;SPE;ST0;FP;SPA;EP;

以(100, 100)為圓心勋陪,150為半徑構(gòu)造一個(gè)圓。然后設(shè)置畫筆顏色‘E'硫兰,透明度為0%诅愚,填充該圓。重新設(shè)置畫筆顏色'A'劫映,為圓描邊违孝。

SPU;SW1;PU100,100;PD200,100;AA200,150,-90;PD250,200;

從(100, 100)開始,畫一條水平線到(200, 100)泳赋;接著以(200, 100)為起點(diǎn)雌桑,以(200, 150)為圓心,順序針畫一個(gè)90度的圓弧祖今,畫筆移到圓弧的終點(diǎn)(將會是(250, 150))校坑;最后畫一條直線到(250, 200)。

SPC;SW3;PU500,500,1000,1000;SCsample99,1;PD1000,500;

移動畫筆從(500, 500)到(1000, 1000)千诬,方位為135度耍目,沿該方向畫一個(gè)符號(sample99),符號的轉(zhuǎn)心位于(1000, 1000)處徐绑;接著從(1000, 1000)邪驮,用顏色'C',寬度為3個(gè)單位的畫筆畫一條直線到(1000, 500)傲茄。

編碼實(shí)現(xiàn)

新建一個(gè)靜態(tài)的工具類S52Tools毅访,其中方法DrawSymbol實(shí)現(xiàn)上述指令:

public static class S52Tools
{
    //ca 畫布
    //colors 顏色字典,索引是SP指令后的字母
    //instructions 矢量符號描述符號組成的指令
    public static void DrawSymbol(SKCanvas ca, Dictionary<char, SKColor> colors, List<string> instructions)
    {
        bool isPMExist = false;
        int width = 1;                            //默認(rèn)寬度
        var color = S52Colors.Instance["NODTA"];  //默認(rèn)顏色
        var pen = new SKPaint() { Color = color, StrokeWidth = width, Style = SKPaintStyle.Stroke }; //默認(rèn)畫筆           
        SKPath path = new SKPath();

        foreach (var command in instructions)
        {
            SKPath path = new SKPath();
            string[] subcomm = command.Split(';');
            foreach (var item in subcomm)
            {
                if (item.Length >= 2)
                {
                    int transparency = 0;
                    switch (item.Substring(0, 2))
                    {
                        case "SP":  //獲取顏色
                            color = colors[item[2]];    
                            pen.Color = color;
                            break;
                        case "ST":  //獲取透明度
                            transparency = item[2] - 48;
                            color = color.WithAlpha((byte)(255 - transparency * 255 / 4));
                            pen.Color = color;
                            break;
                        case "SW":  //獲取畫筆寬度
                            width = (item[2] - 48) * 30; // 30 = 0.3/0.01
                            pen.StrokeWidth = width;
                            break;
                        case "PU":  //起點(diǎn) PU500,500,1000,1000;
                            string[] pu = item.Substring(2).Split(',');
                            for (int i = 0; i < pu.Length-1; i+=2)
                            {
                                path.MoveTo(new SKPoint(int.Parse(pu[i]), int.Parse(pu[i+1])));
                            }
                            break; 
                        case "PD":  //終點(diǎn) PD; PD500,500; PD500,500,1000,1000;
                            string[] pd = item.Substring(2).Split(',');

                            if (pd.Length == 1) //PD;
                            {
                                ca.DrawPoint(path.LastPoint, pen);
                            }
                            else
                            {
                                for (int i = 0; i < pd.Length - 1; i += 2)
                                {
                                    SKPoint end = new SKPoint(int.Parse(pd[i]), int.Parse(pd[i + 1]));
                                    if (path.LastPoint == end)
                                    {
                                        ca.DrawPoint(end, pen);
                                        continue;
                                    }

                                    path.LineTo(end);
                                }
                            }
                            break;
                        case "CI":
                            int radius = int.Parse(item.Substring(2));
                            path.AddCircle(path.LastPoint.X, path.LastPoint.Y, radius);
                            break;
                        case "AA":
                            string[] xyd = item.Substring(2).Split(',');
                            var x = int.Parse(xyd[0]);
                            var y = int.Parse(xyd[1]);
                            var d = int.Parse(xyd[2]);
                            var r = (float)Math.Sqrt(GeoTools.DistanceSqrd(x, y, (int)path.LastPoint.X, (int)path.LastPoint.Y));
                            var rect = new SKRect(x - r, y - r, x + r, y + r);
                            //從正北起算,而繪制時(shí)以正東起算占卧,所以需要減掉90度
                            var startAngle = GeoTools.AngleBetweenTwoPoints((int)path.LastPoint.X, (int)path.LastPoint.Y, x, y);
                            path.ArcTo(rect, (float)startAngle - 90, -1 * d, false);
                            break;
                        case "PM":
                            switch (item[2] - 48)
                            {
                                case 0: isPMExist = true; break;
                                case 1: path.Close(); break;
                                case 2: isPMExist = false; break;
                            }
                            break;
                        case "EP":
                            pen.Style = SKPaintStyle.Stroke;
                            ca.DrawPath(path, pen);
                            break;
                        case "FP":
                            pen.Style = SKPaintStyle.Fill;
                            ca.DrawPath(path, pen);
                            break;
                        case "SC":  //ECDIS中并沒有用到該指令,暫不用解析
                            throw new Exception("S52 [SC]繪制沒有解析");
                        default:
                            throw new Exception($"S52 [{item.Substring(0, 2)}]繪制沒有解析");
                    }
                }
            }
        }

        if (!isPMExist)
        {
            pen.Style = SKPaintStyle.Stroke;
            ca.DrawPath(path, pen);
        }
    }
}

調(diào)用過程及繪制結(jié)果如下:

    ca.Scale(0.1f);
    S52Tools.DrawSymbol(
        ca, //畫布
        new Dictionary<char, SKColor> { //顏色字典
            { 'A', SKColors.Red },
            { 'B', SKColors.Blue },
            { 'C', SKColors.Green },
        },
        new List<string>() { //指令
            "SPB;ST2;PM0;PU1000,1000;PD1000,2000,2000,2000,2000,1000;PM2;FP;",
            "SPA;SW2;PU1000,1000;PD1000,2000,2000,2000,2000,1000,1000,1000;",
            "SPC;SW2;PU1000,1000;PD2000,1000;AA2000,1500,-90;PD2500,2000;",
        }
    );
矢量符號指令繪制結(jié)果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涌攻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弛饭,死亡現(xiàn)場離奇詭異,居然都是意外死亡萍歉,警方通過查閱死者的電腦和手機(jī)侣颂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枪孩,“玉大人憔晒,你說我怎么就攤上這事∶镂瑁” “怎么了拒担?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長攻询。 經(jīng)常有香客問我从撼,道長,這世上最難降的妖魔是什么钧栖? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任低零,我火速辦了婚禮,結(jié)果婚禮上拯杠,老公的妹妹穿的比我還像新娘掏婶。我一直安慰自己,他們只是感情好潭陪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布雄妥。 她就那樣靜靜地躺著,像睡著了一般畔咧。 火紅的嫁衣襯著肌膚如雪茎芭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天誓沸,我揣著相機(jī)與錄音梅桩,去河邊找鬼。 笑死拜隧,一個(gè)胖子當(dāng)著我的面吹牛宿百,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播洪添,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼垦页,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了干奢?” 一聲冷哼從身側(cè)響起痊焊,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后薄啥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辕羽,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年垄惧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刁愿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡到逊,死狀恐怖铣口,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情觉壶,我是刑警寧澤脑题,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站掰曾,受9級特大地震影響旭蠕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旷坦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一掏熬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秒梅,春花似錦旗芬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辆它,卻和暖如春誊薄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锰茉。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工呢蔫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人飒筑。 一個(gè)月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓片吊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親协屡。 傳聞我的和親對象是個(gè)殘疾皇子俏脊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容