ArcGIS10.2服务切片 JPG/PNG 转换为 Bundle/Bundlx

来源:互联网 发布:淘宝网店推广软文 编辑:程序博客网 时间:2024/04/30 13:50

本文编写主要参考博客:

http://blog.csdn.net/abc553226713/article/details/8668839


Bundle文件与Bundlx文件内容分析

Bundle文件:

4字节存储长度 + 图片Byte[]循环存储

最大存储16384(128*128)这样的循环

可以看出参考文章中Bundle文件只存图片数据

*更正*

参考文章没写完全,10.2的Bundle前面有60个字节是描述性文字

已尝试将其全部置0,ArcServer依然可以正确显示图片

正确格式是:

60字节描述头 + (4字节存储长度 + 图片Byte[] 循环存储)


Bundlx文件:

内容结构 16byte +81920byte + 16byte

其中2个16字节 文件头信息与尾信息 属于描述性注记 内容无用

经实践证明故意修改ArcServer自带创建的Bundlx文件将前后16位描述性注记写为空的时候ArcServer也不会去理会,依然可以正确读取数据

 

中间的81920字节文件主体信息 内容包含:

81920 = 5*128*128   5字节存储偏移量 有128*128个5字节

所以每个Bundle文件最多存储128*128 = 16384个子文件

另外要注意这个5字节对应的JPG文件是按列排序的不是按行


注意:由于Bundle文件在文件头有60字节的描述,所以Bundlx文件默认第一个的偏移量一定是60


Bundlx文件偏移量解释

偏移量是描述一个图片在Bundle文件中的起点,偏移量由5位Buffer转Int数值,读取偏移量参考代码:

long offset =(long)(buffer[0]& 0xff) + (long)(buffer[1] & 0xff)

                       *256 + (long)(buffer[2]& 0xff) * 65536

                      + (long)(buffer[3] &0xff) * 16777216

                       + (long)(buffer[4]& 0xff) * 4294967296L;

这个表示方法表示偏移量是由低位往高位进行表示的

比如byte[5]{0,1,0,0,0}= 偏移 1 * 256 length

又如byte[5]{0,1,5,0,0}= 偏移 1 * 256 + 5 * 256 * 256 length

每一位表示 位值 * 256的位数开方将所有位相加得到位值

同理Bundle文件的4字节存储长度也是这个表达方式

实质上就是一个256进制与10进制的转换过程(因为一个Byte只能存256个不同的值,最大化利用)


Bundle/Bundlx文件命名规则解释

R+4位16进制数字+C+4位16进制数字组成

比如R0080C0180表示这个紧凑格式表达了

第129-256行384-512列

行列顺序从指定的切片原点算(这个与普通切片相同)

 

现在对同一范围同比例尺进行ArcServer紧凑切片与切片工具切片

发现Jpg起点与Bundle起点相差了一些

R2380C1280 转十进制表示 切片范围左上角起点:

9088行 4736列

R23cdC12b6 转十进制表示 切片范围左上角起点:

9165行 4790 列

出现差异是因为Jpg是由原点以Step为1递增到实际位置,实际范围的起点位置更为精确

而Bundle是由原点以Step为128递增到实际位置(行列数总是128的倍数)

 

所以要解决这个问题,必须重算更新的Jpg文件在所归属的Bundle文件范围,并将这些文件注册到这些范围中,类似注册空间索引,然后根据这些注册信息,将数据写入Bundle


如何实现切片成果JPG转Bundle功能

第一步 获取更新图片JPG文件计算每个文件的十进制行列

第二步 获取切片原点 计算包含此更新区域的Bundle文件命名 以及十进制的行列范围

第三步 写Bundle文件

3.1匹配每个JPG文件到Bundle范围中

3.2写入Jpg的大小并转高低位保存在头4个Byte中(注意,如果图片内容为空,依然要填写4个0000补齐位数)

3.3写入Byte[] 内容到Bundle文件中

3.4记录每个文件的行列号和并记录每个文件的序号与长度

第四步 写Bundlx文件

根据写Bundle产生的信息,依次按列填写每个文件的偏移量


这里个功能不仅可以从JPG转换到Bundle 而且还可以对已有Bundle进行JPG更新


★转换全代码★

使用 只需要指定 JPG的Path 与目标Bundle文件的Path 即可 调用样例代码

        static void Main(string[] args)
        {
            PackageBundle BundleMaker = new PackageBundle();
            BundleMaker._strJPGPath = @"C:\JPGList";
            BundleMaker._strBundlePath = @"C:\_alllayers0";
            BundleMaker.StartPackage();
        }


class PackageBundle
    {
        public string _strJPGPath;
        public string _strBundlePath;


        private CommonTools _tool = new CommonTools();
        public void StartPackage()
        {
            try
            {
                if (!new DirectoryInfo(_strBundlePath).Exists)
                    new DirectoryInfo(_strBundlePath).Create();
                //读取一个Jpg文件夹,并将其组合为Bundle/Bundlx文件


                //第一步 依次遍历每个L00 L01 级别
                DirectoryInfo RootDir = new DirectoryInfo(_strJPGPath);//C:\Users\chop\Desktop\_alllayers
                DirectoryInfo[] LevelDirList = RootDir.GetDirectories();
                foreach (DirectoryInfo LevelDir in LevelDirList)
                {
                    int MinRow,MinColumn,MaxRow,MaxColumn;
                    //获取JPG内容的范围
                    GetJpgNamingEnv(LevelDir.FullName,out MinRow,out MinColumn,out MaxRow,out MaxColumn);//C:\Users\chop\Desktop\_alllayers\L00
                    //获取Bundle文件
                    List<Bundle> BundleList = CreateBundleList(_strBundlePath + "\\" + LevelDir.Name, MinRow, MinColumn, MaxRow, MaxColumn);
                    //匹配JPG文件到Bundle文件
                    AttachJPGToBundle(LevelDir.FullName,BundleList);
                    //开始构造Bundle Bundlx文件
                    CreateBundleFiles(BundleList);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                Console.WriteLine("打包完毕");
                Console.ReadKey();
            }
        }
 
        private void GetJpgNamingEnv(string strJPGLevelPath,out int MinRow,out int MinColumn,out int MaxRow,out int MaxColumn)
        {
            MinRow = 0; MinColumn = 0; MaxRow = 0; MaxColumn = 0;


            //获取所有文件夹 他们代表着行
            DirectoryInfo[] RowDirList = _tool.GetAllDirectory(strJPGLevelPath);
            //获取所有文件 他们代表着列
            FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);


            //翻译每一个文件夹的名称(行)到 十进制 并且排序
            List<int> RowList = new List<int>();
            foreach (DirectoryInfo RowDir in RowDirList)
            {
                string Num16 = RowDir.Name.TrimStart('R');
                string Num10 = _tool.To10(Num16);
                RowList.Add(int.Parse(Num10));
            }
            RowList.Sort();
            MinRow = RowList[0];
            MaxRow = RowList[RowList.Count - 1];


            List<int> ColumnList = new List<int>();
            foreach (FileInfo Fileinfo in ColumnFileList)
            {
                string Num16 = Fileinfo.Name.TrimStart('C').TrimEnd(Fileinfo.Extension.ToCharArray());
                string Num10 = _tool.To10(Num16);
                ColumnList.Add(int.Parse(Num10));
            }
            ColumnList.Sort();
            MinColumn = ColumnList[0];
            MaxColumn = ColumnList[ColumnList.Count - 1];
        }


        class Bundle
        {
            public string strFileName;
            public int MinRow;
            public int MinColumn;
            public int MaxRow;
            public int MaxColumn;
            public Bundle()
            {
                JPGList = new List<FileInfo>();
                JPGIDList = new List<string>();
            }
            public List<FileInfo> JPGList;
            public List<string> JPGIDList;
        }


        //根据JPG文件最大最小的行列 创建Bundle集合
        private List<Bundle> CreateBundleList(string strBundleLevelDir,int MinRow, int MinColumn, int MaxRow, int MaxColumn)
        {
            //Bundle文件是行列为128个小JPG组成的 每行每列以128为Step从原点递增


            //这里获取一个范围的Bundle 需要从最小的行列 开始计算起始Bundle


            //然后由起始Bundle递增128 来增加Bundle数量 只要递增的Bundle的起始行列 始终在MaxRow MaxColumn范围内 那么这个Bundle就是有效Bundle
            int Size = 128;
            int BundleRow = (MinRow / Size) * Size;
            int BundleColumn = (MinColumn / Size) * Size;


            //这里建立起来的Bundle有可能是无效Bundle(因为更新区域不可能是规则的矩形),即不包含任何Jpg的Bundle,先不管,到后面判定时再删除这些Bundle
            List<Bundle> BundleList = new List<Bundle>();
            int BundleColumnMin = BundleColumn;


            if (!new DirectoryInfo(strBundleLevelDir).Exists)
                new DirectoryInfo(strBundleLevelDir).Create();


            while(BundleRow < MaxRow)
            {
                while(BundleColumn < MaxColumn)
                {
                    Bundle bundle = new Bundle();
                    bundle.MinRow = BundleRow;
                    bundle.MaxRow = BundleRow + Size;
                    bundle.MinColumn = BundleColumn;
                    bundle.MaxColumn = BundleColumn + Size;


                    bundle.strFileName = strBundleLevelDir + "\\" + "R" + _tool.To16(bundle.MinRow.ToString()) + "C" + _tool.To16(bundle.MinColumn.ToString()) + ".bundle";


                    BundleList.Add(bundle);
                    BundleColumn = BundleColumn + Size;
                }
                BundleColumn = BundleColumnMin;
                BundleRow = BundleRow + Size;
            }
            return BundleList;
        }


        //将每个JPG归属到Bundle中
        private void AttachJPGToBundle(string strJPGLevelPath, List<Bundle> BundleList)
        {
            //获取所有文件
            FileInfo[] ColumnFileList = _tool.GetAllFiles(strJPGLevelPath);


            //获取每个文件所归属的行列 用十进制表示
            foreach (FileInfo colFile in ColumnFileList)
            {
                int dColumnNum = int.Parse(_tool.To10(colFile.Name.Substring(5, 4)));
                int dRowNum = int.Parse(_tool.To10(colFile.DirectoryName.Substring(colFile.DirectoryName.Length - 4, 4)));
                foreach (Bundle bundleFile in BundleList)
                {
                    if ((bundleFile.MinRow <= dRowNum && dRowNum < bundleFile.MaxRow) && (bundleFile.MinColumn <= dColumnNum && dColumnNum < bundleFile.MaxColumn))
                    {
                        bundleFile.JPGList.Add(colFile);
                        bundleFile.JPGIDList.Add(dRowNum.ToString() + "-" + dColumnNum.ToString());
                    }
                }
            }


            //除去冗余Bundle
            for (int i = 0; i < BundleList.Count; i++)
            {
                if (BundleList[i].JPGList.Count == 0)
                {
                    BundleList.RemoveAt(i);
                    i--;
                }
            }
        }


        private void CreateBundleFiles(List<Bundle> BundleList)
        {
            //从第一列128个开始往下写,写到N列 每次写到算好对应的16进制文件名并组成路径与List已有对比
            foreach (Bundle bundleFile in BundleList)
            {
                //第一种情况Bundle不存在,这时需要从Bundle文件直接写入
                if (!new FileInfo(bundleFile.strFileName).Exists)
                {
                    WriteBundleFile(bundleFile);
                }
                //第二种情况Bundle已经存在 这时需要从Bundlx文件开始分析写入
                else
                {
                    RewriteBundleFile(bundleFile);
                }
            }
        }


        private void WriteBundleFile(Bundle bundleFile)
        {
            FileStream FsBundle = new FileStream(bundleFile.strFileName, FileMode.CreateNew, FileAccess.Write);
            FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.CreateNew, FileAccess.Write);
            FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0
            FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0 


            byte[] emptyBytes = new byte[60];
            for (int ie = 0; ie < 60; ie++)
                emptyBytes[ie] = 88;
            FsBundle.Write(emptyBytes, 0, 60);
            FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte


            int dOffset = 60;
            for (int i = 0; i < 128; i++)
            {
                for (int j = 0; j < 128; j++)
                {
                    string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();
                    int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);
                    //写空白图片
                    if (dJPGIndex == -1)
                    {
                        FsBundle.Write(OffsetToByte4(0), 0, 4);


                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);


                        dOffset = dOffset + 4;
                    }
                    //写实际图片
                    else
                    {
                        byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);


                        FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);


                        FsBundle.Write(JPGBytes, 0, JPGBytes.Length);


                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);


                        dOffset = dOffset + 4 + JPGBytes.Length;
                    }
                }
            }
            FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0 
            FsBundle.Close();
            FsBundlx.Close();
        }


        private void RewriteBundleFile(Bundle bundleFile)
        {
            FileStream FsBundle = new FileStream(bundleFile.strFileName + ".Buffer", FileMode.CreateNew, FileAccess.Write);
            FileStream FsBundlx = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer", FileMode.CreateNew, FileAccess.Write);


            FileStream FsBundleSource = new FileStream(bundleFile.strFileName, FileMode.Open, FileAccess.Read);
            FileStream FsBundlxSource = new FileStream(bundleFile.strFileName.TrimEnd('e') + "x", FileMode.Open, FileAccess.Read);


            FsBundle.Seek(0, SeekOrigin.Begin);//前60个描述内容统一置0
            FsBundlx.Seek(0, SeekOrigin.Begin);//前16个描述内容统一置0 


            FsBundleSource.Seek(0, SeekOrigin.Begin);
            FsBundlxSource.Seek(0, SeekOrigin.Begin);


            byte[] emptyBytes = new byte[60];
            for (int ie = 0; ie < 60; ie++)
                emptyBytes[ie] = 88;
            FsBundle.Write(emptyBytes, 0, 60);
            FsBundlx.Write(emptyBytes, 0, 16);//Bundlx的起点是60 因为Bundle前60个是描述Byte


            int dOffset = 60;
            for (int i = 0; i < 128; i++)//column
            {
                for (int j = 0; j < 128; j++)//row
                {
                    string strCurrentID = (bundleFile.MinRow + j).ToString() + "-" + (bundleFile.MinColumn + i).ToString();
                    int dJPGIndex = bundleFile.JPGIDList.IndexOf(strCurrentID);
                    //写已有Bundle内容图片
                    if (dJPGIndex == -1)
                    {
                        byte[] JPGBytes = GetBundleData(FsBundleSource, FsBundlxSource, j, i);


                        if (JPGBytes.Length == 0)
                        {
                            FsBundle.Write(OffsetToByte4(0), 0, 4);


                            FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);


                            dOffset = dOffset + 4;
                        }
                        else
                        {
                            FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);


                            FsBundle.Write(JPGBytes, 0, JPGBytes.Length);


                            FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);


                            dOffset = dOffset + 4 + JPGBytes.Length;
                        }
                    }
                    //写实际图片
                    else
                    {
                        byte[] JPGBytes = ReadJPGByte(bundleFile.JPGList[dJPGIndex].FullName);


                        FsBundle.Write(OffsetToByte4(JPGBytes.Length), 0, 4);


                        FsBundle.Write(JPGBytes, 0, JPGBytes.Length);


                        FsBundlx.Write(OffsetToByte5(dOffset), 0, 5);


                        dOffset = dOffset + 4 + JPGBytes.Length;
                    }
                }
            }
            FsBundlx.Write(emptyBytes, 0, 16);//别忘了添加后16个描述内容 后2个描述内容统一置0 
            FsBundle.Close();
            FsBundlx.Close();
            FsBundleSource.Close();
            FsBundlxSource.Close();


            //全部写入完毕后 还需要将Buffer文件替换到实际命名的文件;
            new FileInfo(bundleFile.strFileName).Delete();
            new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x").Delete();
            new FileInfo(bundleFile.strFileName + ".Buffer").MoveTo(bundleFile.strFileName);
            new FileInfo(bundleFile.strFileName.TrimEnd('e') + "x" + ".Buffer").MoveTo(bundleFile.strFileName.TrimEnd('e') + "x"); ;
        }


        private byte[] GetBundleData(FileStream FsBundleSource, FileStream FsBundlxSource,int dRow,int dColomn)
        {
            int dIndex = dColomn * 128 + dRow;
            FsBundlxSource.Seek(16 + 5 * dIndex, SeekOrigin.Begin);
            byte[] buffer = new byte[5];
            FsBundlxSource.Read(buffer, 0, 5);
            long offset = (long)(buffer[0] & 0xff) + (long)(buffer[1] & 0xff)
                    * 256 + (long)(buffer[2] & 0xff) * 65536
                   + (long)(buffer[3] & 0xff) * 16777216
                    + (long)(buffer[4] & 0xff) * 4294967296L;


            FsBundleSource.Seek(offset, SeekOrigin.Begin);
            byte[] lengthBytes = new byte[4];
            FsBundleSource.Read(lengthBytes, 0, 4);
            int length = (int)(lengthBytes[0] & 0xff)
                    + (int)(lengthBytes[1] & 0xff) * 256
                    + (int)(lengthBytes[2] & 0xff) * 65536
                    + (int)(lengthBytes[3] & 0xff) * 16777216;
            byte[] result = new byte[length];
            FsBundleSource.Read(result, 0, length);
            return result;
        }


        //10进制转256进制 4位表示法
        private byte[] OffsetToByte4(int dOffset)
        {
            return System.BitConverter.GetBytes(dOffset);
        }


        //10进制转256进制 5位表示法
        private byte[] OffsetToByte5(long dOffset)
        {
            byte[] byte8 = System.BitConverter.GetBytes(dOffset);
            byte[] byte5 = new byte[] { byte8[0], byte8[1], byte8[2], byte8[3], byte8[4] };
            return byte5;
        }


        //读取文件字节流到Byte[]
        private byte[] ReadJPGByte(string strJPGPath)
        {
            using (FileStream fsRead = new FileStream(strJPGPath, FileMode.Open, FileAccess.Read))
            {
                byte[] Buffer = new byte[fsRead.Length];
                fsRead.Read(Buffer, 0, Buffer.Length);
                return Buffer;
            }
        }
    }


阅读全文
0 0
原创粉丝点击