开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码)

2022-11-02,,,,

系列文章目录

开源方案搭建可离线的精美矢量切片地图服务-1.开篇(附成果演示地址)

开源方案搭建可离线的精美矢量切片地图服务-2.PostGIS+GeoServer矢量切片

开源方案搭建可离线的精美矢量切片地图服务-3.Mapbox个性化地图定制入门

开源方案搭建可离线的精美矢量切片地图服务-4.Mapbox样式设计

开源方案搭建可离线的精美矢量切片地图服务-5.Mapbox离线项目实现

开源方案搭建可离线的精美矢量切片地图服务-6.Mapbox之.pbf字体库

开源方案搭建可离线的精美矢量切片地图服务-7.Arcgis Pro企业级应用

开源方案搭建可离线的精美矢量切片地图服务-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保存名称及位置信息,下图图展示的是小图标与大图文件的示例。下面我讲一下两种文件互转。

   <=互转=>    

下面是这次项目实现的功能,包含Sprite大图的合成与分割,也包含对单个图标文件的宽度与高度的调整。

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画布中,实现成果与代码如下:

转换为单个文件

         //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 = ; i < obj.Count; i++)
{
if (item == null)
{
item = obj.First;
}
else
{
item = item.Next;
}
Param p = new Param();
p.name = item.Path.Substring(, item.Path.Length - ).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 = ; i < itemp.width; i++)
{
for (int j = ; 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这样规则的图标进行调整,例如

宽度增加20px

实现思路:我们选择规则图形的中心线,宽度调整就是以中心线进行左右拉伸复杂增加宽度,实现代码如下:

        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 / ;
for (int i = ; i < map.Width; i++)
{
for (int j = ; j < map.Height; j++)
{
//获取像素
Color color = map.GetPixel(i, j);
if (i == centernum)
{
editMap.SetPixel(i, j, color);
if (addnum > )
{
for (int m = ; m < addnum; m++)
{
editMap.SetPixel(i + m + ,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)循环小图标,在大图中画出小图标,并记录位置信息。

实现成果与代码如下:

转换为sprite文件

     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 = ;
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 = ;
for (int i = ; i < paramlist.Count; i++)
{
countnum += paramlist[i].width;
if (countnum > widthnum)
{
i = i - ;
countnum = ;
rowparams.Add(paramnowlist);
paramnowlist = new List<Param>();
}
else
{
paramnowlist.Add(paramlist[i]);
}
if (i == paramlist.Count - )
{
rowparams.Add(paramnowlist);
break;
}
}
//计算应有的高度
int allheight = ;
foreach (var item in rowparams)
{
allheight += item.Select(m => m.height).Max();
}
string spritejson = "{";
//开始画大图
using (Bitmap editMap = new Bitmap(widthnum, allheight))
{
//保存起始高度
int heighttemp = ;
for (int i = ; i < rowparams.Count; i++)
{
int tempwidthnum = ;
for (int j = ; j < rowparams[i].Count; j++)
{
using (Bitmap map = (Bitmap)Image.FromFile(System.Environment.CurrentDirectory + "/" + rowparams[i][j].name + ".png"))
{
//循环小图片
for (int x = ; x < map.Width; x++)
{
for (int y = ; 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

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码)的相关教程结束。

《开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码).doc》

下载本文的Word格式文档,以方便收藏与打印。