開源方案搭建可離線的精美矢量切片地圖服務-8.mapbox 之sprite大圖圖標文件生成(附源碼)

項目成果展示(所有項目文件都在阿里雲的共享雲虛擬主機上,訪問地圖可以會有點慢,請多多包涵)。

01:中國地圖:http://test.sharegis.cn/mapbox/html/3china.html

02:德國-德累斯頓市:http://test.sharegis.cn/mapbox/html/6germany.html

1.什麼是sprite文件

      sprite 文件主要是將一堆小圖生成一種大圖的方法,並且將每張小圖的位置信息保存下來,方便讀取。在網絡請求中會減少請求的數量,mapbox借鑑前端中CSS Sprite方法存儲圖標信息的。sprite.png文件保存圖標,sprite.json保存名稱及位置信息,下圖圖展示的是小圖標與大圖文件的示例。下面我講一下兩種文件互轉。

image   <=互轉=>     image

     下面是這次項目實現的功能,包含Sprite大圖的合成與分割,也包含對單個圖標文件的寬度與高度的調整。

image

2.sprite大圖轉小圖

      我們上一節講到是使用arcgis pro會將.mxd地圖配圖文件轉爲mapbox的樣式文件,同樣會生成sprite.png和sprite.json的圖標文件。由於arcgis字體庫的限制,生成的圖標可能不符合的要求,我們有修改圖標的需要,這裏我們不僅要替換大圖中的小圖標而且要記錄圖標的的位置信息到sprite.json中。下面寫了一個從大圖文件中生成單個小圖標文件的分割功能。

      實現思路:首先讀取大圖圖片,然後根據json文件中的位置信息,json中的圖標信息如下,xy代表圖標的左上角在大圖中的位置,width height代表圖標的大小,pixelRatio代表像素單位,spriteicon/county爲文件名。

"spriteicon/county": {
     "x": 75,
     "y": 0,
     "width": 32,
     "height": 14,
     "pixelRatio": 1,
     "sdf": false
}

讀取單個圖標文件的像素信息,寫到一個新建的Bitmap畫布中,實現成果與代碼如下:

image 轉換爲單個文件image

         //sprite json文件
            string text = ReadFile(textBox3.Text);
            JObject obj = JObject.Parse(text);
            JToken item = null;
            //將json轉爲對象
            List<Param> paramlist = new List<Param>();
            for (int i = 0; i < obj.Count; i++)
            {
                if (item == null)
                {
                    item = obj.First;
                }
                else
                {
                    item = item.Next;
                }
                Param p = new Param();
                p.name = item.Path.Substring(2, item.Path.Length - 4).Replace("/", "-").Replace(":", "&");
                p.x = (int)item.First["x"];
                p.y = (int)item.First["y"];
                p.width = (int)item.First["width"];
                p.height = (int)item.First["height"];
                paramlist.Add(p);
            }
            using (Bitmap map = (Bitmap)Image.FromFile(textBox3.Text+@"\sprite.png"))
            {

                using (Bitmap editMap = new Bitmap(map, map.Width, map.Height))
                {
                    foreach (var itemp in paramlist)
                    {
                        //保存圖片的畫布
                        Bitmap itemMap = new Bitmap(itemp.width, itemp.height);
                        for (int i = 0; i < itemp.width; i++)
                        {
                            for (int j = 0; j < itemp.height; j++)
                            {
                                //獲取像素
                                Color color = editMap.GetPixel(itemp.x + i, itemp.y + j);
                                itemMap.SetPixel(i, j, color);
                            }
                        }
                        //保存
                        string savepath = System.Environment.CurrentDirectory + @"\spriteicon" + itemp.name+ ".png";
                        itemMap.Save(savepath);
                    }
                }
            }

3.小圖標的調整

      對於一些規則的小圖標,例如標記路面信息的label,他的寬度由路的屬性信息決定,展示我們要對不同長度的文字設置不同大小label這裏我們要對多個圖標的寬度和高度進行調整,這是隻是對lable這樣規則的圖標進行調整,例如

image 寬度增加20pximage

實現思路:我們選擇規則圖形的中心線,寬度調整就是以中心線進行左右拉伸複雜增加寬度,實現代碼如下:

        DirectoryInfo folder = new DirectoryInfo(System.Environment.CurrentDirectory);
            List<string> filenames = new List<string>();
            int addnum = Convert.ToInt32(textBox2.Text);
            foreach (var NextFolder in folder.GetFiles("*.png"))
            {
                if (NextFolder.Name.Contains(textBox1.Text))
                {
                    filenames.Add(NextFolder.Name);
                }
            }
            foreach (var item in filenames)
            {
                using (Bitmap map = (Bitmap)Image.FromFile(System.Environment.CurrentDirectory + "/" + item))
                {
                    using (Bitmap editMap = new Bitmap(map.Width + addnum, map.Height ))
                    {
                        int centernum = map.Width / 2;
                        for (int i = 0; i < map.Width; i++)
                        {
                            for (int j = 0; j < map.Height; j++)
                            {
                                //獲取像素
                                Color color = map.GetPixel(i, j);
                                if (i == centernum)
                                {
                                    editMap.SetPixel(i, j, color);
                                    if (addnum > 0)
                                    {
                                        for (int m = 0; m < addnum; m++)
                                        {
                                            editMap.SetPixel(i + m + 1,j, color);
                                        }
                                    }
                                }
                                else if (i < centernum)
                                {
                                    editMap.SetPixel(i, j, color);
                                }
                                else
                                {
                                    editMap.SetPixel(i + addnum,j, color);
                                }
                            }
                        }
                        //保存
                        string savepath = System.Environment.CurrentDirectory + @"\result\" + item;
                        editMap.Save(savepath);
                    }
                }
            }

4.小圖轉sprite大圖

      將小圖標合成一張sprite大圖並在sprite.json中記錄生成的位置信息,這裏最主要的就是圖標的擺放規則,

(1)獲取所有的圖標文件,按照高度從小到大排列

(2)根據大圖生成的默認寬度,循環小圖片,形成一行一行的圖片集合。

(3)根據行數和寬度生成大圖的寬度。

(4)循環小圖標,在大圖中畫出小圖標,並記錄位置信息。

實現成果與代碼如下:

image 轉換爲sprite文件image

     DirectoryInfo folder = new DirectoryInfo(System.Environment.CurrentDirectory);
            List<Param> paramlist = new List<Param>();
            foreach (var NextFolder in folder.GetFiles("*.png"))
            {
                using (Bitmap map = (Bitmap)Image.FromFile(System.Environment.CurrentDirectory + "/" + NextFolder.Name))
                {
                    Param p = new Param();
                    p.name = NextFolder.Name.Replace(".png", "");
                    p.width = map.Width;
                    p.height = map.Height;
                    paramlist.Add(p);
                }
            }
            //圖片默認寬度爲255,
            int widthnum = 255;
            paramlist = paramlist.OrderBy(m => m.name).OrderBy(m => m.height).ToList();
            //一行一行的圖片集合
            List<List<Param>> rowparams = new List<List<Param>>();
            List<Param> paramnowlist = new List<Param>();
            int countnum = 0;
            for (int i = 0; i < paramlist.Count; i++)
            {
                countnum += paramlist[i].width;
                if (countnum > widthnum)
                {
                    i = i - 1;
                    countnum = 0;
                    rowparams.Add(paramnowlist);
                    paramnowlist = new List<Param>();
                }
                else
                {
                    paramnowlist.Add(paramlist[i]);
                }
                if (i == paramlist.Count - 1)
                {
                    rowparams.Add(paramnowlist);
                    break;
                }
            }
            //計算應有的高度
            int allheight = 0;
            foreach (var item in rowparams)
            {
                allheight += item.Select(m => m.height).Max();
            }
            string spritejson = "{";
            //開始畫大圖
            using (Bitmap editMap = new Bitmap(widthnum, allheight))
            {
                //保存起始高度
                int heighttemp = 0;
                for (int i = 0; i < rowparams.Count; i++)
                {
                    int tempwidthnum = 0;
                    for (int j = 0; j < rowparams[i].Count; j++)
                    {
                        using (Bitmap map = (Bitmap)Image.FromFile(System.Environment.CurrentDirectory + "/" + rowparams[i][j].name + ".png"))
                        {
                            //循環小圖片
                            for (int x = 0; x < map.Width; x++)
                            {
                                for (int y = 0; y < map.Height; y++)
                                {
                                    //獲取像素
                                    Color color = map.GetPixel(x, y);
                                    editMap.SetPixel(x+ tempwidthnum, y+ heighttemp, color);
                                }
                            }
                        }

                        spritejson += "\""+ rowparams[i][j].name.Replace("-", "/").Replace("&",":") + "\":{\"x\":";
                        spritejson += tempwidthnum + ",\"y\":" + heighttemp + ",\"width\":" + rowparams[i][j].width;
                        spritejson += ",\"height\":" + rowparams[i][j].height + ",\"pixelRatio\":1,\"sdf\":false},";
                       //增加寬度
                       tempwidthnum += rowparams[i][j].width;
                    }
                    heighttemp += rowparams[i].Select(m => m.height).Max();
                }
                //保存大圖
                string savepath = System.Environment.CurrentDirectory + @"\result\sprite.png";
                editMap.Save(savepath);
            }
            spritejson= spritejson.TrimEnd(',');
            spritejson += "}";
            //寫入文件
            using (StreamWriter fw= new StreamWriter(System.Environment.CurrentDirectory + @"\result\sprite.json"))
            {
                fw.WriteLine(spritejson);
            }

5.結尾

     我們這裏的sprite大圖生成了sprite.json來保存位置信息,其實css sprite的實現也是類似的。使用圖片定位技術來實現的,例如像下面的這段css代碼一樣來對圖片做定位的,大家可以簡單修改一下源碼便可實現,這裏就不多介紹了。

.bg-spriteicon {
    width: 17px; height: 17px;
    background: url('css_sprites.png') -138px -47px;
}

源碼地址:鏈接:https://pan.baidu.com/s/1tLihDaZFa--xFCzI42tfEA 密碼:f3dm

github地址:https://github.com/HuHongYong/Mapbox-sprite-generation

作者:ATtuing

出處:http://www.cnblogs.com/ATtuing

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章