当前位置: 首页 > news >正文

ArcGIS 切片缓存紧凑文件格式分析与使用

在ArcGIS 10中出现了一种新的切片缓存文件格式:紧凑型存储(Compact)。与之前的松散型存储(Exploded)相比,它有迁移方便、创建更快、减少存储空间等诸多优点,已经成为了创建切片缓存的默认格式。对于本身ArcGIS的产品而言,访问紧凑型存储与访问松散型存储没有任何区别,但是,如果第三方应用想访问新的切片格式,目前官方给出了“不可以”的答复:

The internal architecture of the bundle is not publicly documented by ESRI. If you've coded your own logic to pull tiles out of a virtual directory, you should continue to use the "exploded" format which stores each tile as a single file and was the only option at ArcGIS Server versions 9.3.1 and previous.

我Google了一下,也没有任何相关的资料,因此索性自力更生,自己分析一下紧凑型存储的格式,相信这是目前可以找到的关于紧凑型存储内部格式的唯一资料。

紧凑型存储的原理

紧凑型存储最主要的两种文件是bundle和bundlx文件,其中bundle文件用以存储切片数据,bundlx是bundle文件中切片数据的索引文件。

一个bundle文件中最多可以存储128×128(16384)个切片,但是创建切片缓存并不是一张张切片单独生成,而是以4096像素(无抗锯齿)或2048像素(有抗锯齿)为边长渲染的,如果我们选择的切片边长为256像素并开启了抗锯齿,那么每次ArcSOC进程创建的是一张以8×8(64)个切片拼接成的大图,然后切割后存入bundle文件中。

下图中,蓝色边框代表的是bundle文件,黑色格子是生成切片时拼接的大图,具体的每个切片在黑色格子中,图中并没有显示出来。

clip_image001

存储格式的分析

在分析紧凑型存储格式之前,我首先问自己,如果你要在一个bundle文件中存储内容,同时通过一个bundlx文件中存放索引应该怎么做?中规中矩的做法就是参考数据库的位图索引方式,在bundlx文件中用固定的几个字节标识一个切片在bundle文件中的状态(存储的偏移量和长度)。

观察ArcGIS生成的bundlx文件,每个文件都是一样的大小:81952字节。上面已经提到,每个bundle文件中最多存储16384个切片,虽然bundle文件中可能并没有这么多切片,但是,我猜测bundlx文件中必然是保留了所有者16384个切片的索引位置。粗略估计每个切片会占据大约5个字节,16384×5=81920字节,还多出32字节,猜测存储bundlx文件的标识信息。

通过对一个很存储切片很稀疏的bundlx文件的规律进行观察和猜测,确定了bundlx中文件起始16字节和文件结束16字节与索引无关,剩余的81920字节数据以5个字节的频率重复,构成了一个对bundle文件的索引。

本来以为这5个字节会保存bundle文件中切片数据的偏移和长度,但是发现5个字节表达的信息量可能不够,因此,我同时对bundle中的切片数据进行了一个分析。

我猜想文件并没有进行压缩处理,因此在文件中搜索PNG文件的文件头0x89504E47(我在创建缓存时选择了PNG24格式),发现果然如此。同时,每2个切片数据之间相隔了4个字节(切片数据我是用Exploded的图片直接进行比较的),通过猜想、尝试,发现这4个字节正好是以低位到高位的方式标示了后续这个切片数据的长度。

既然切片数据长度是在bundle文件中记录的,那么在bundlx文件中索引的必然只包括切片数据的偏移量,经过实验发现,bundlx中的5个字节也是以低位到高位的方式标示了数据的偏移量。

切片数据长度和数据偏移猜想应该是无符号的整数,后面的实践证明了这一点。

还有一个问题,bundlx中的每5个字节标示的到底是哪个切片的数据偏移?我的实验的结果是:按列排序:

1

129

2

130

3

131

128

256

16384

从上面的分析,我们如果知道了一个切片的级别、行号、列号,就可以通过bundlx首先找到bundle中切片内容的偏移,然后从bundle文件中取出4个字节的长度数据,再随后根据这个长度读取真实的切片数据。关于如何计算切片的行号、列号,以及bundle文件的命名方式,相对比较简单,这里就不详细叙述了。

使用第三方代码访问切片

基于对紧凑型切片存储格式的分析,我用Java写了一个服务进行验证。我这个服务接受3个参数,和ArcGIS Server Rest接口保持,分别是level、row、col,通过下面的代码从紧凑型切片存储中提取切片数据:

String l = "0" + level;

int lLength = l.length();

if (lLength > 2) {

l = l.substring(lLength - 2);

}

l = "L" + l;

int rGroup = 128 * (row / 128);

String r = "000" + Integer.toHexString(rGroup);

int rLength = r.length();

if (rLength > 4) {

r = r.substring(rLength - 4);

}

r = "R" + r;

int cGroup = 128 * (col / 128);

String c = "000" + Integer.toHexString(cGroup);

int cLength = c.length();

if (cLength > 4) {

c = c.substring(rLength - 4);

}

c = "C" + c;

String bundleBase = String

.format("%s/%s/%s%s", bundlesDir, l, r, c);

String bundlxFileName = bundleBase + ".bundlx";

String bundleFileName = bundleBase + ".bundle";

int index = 128 * (col - cGroup) + (row - rGroup);

FileInputStream isBundlx = new FileInputStream(bundlxFileName);

isBundlx.skip(16 + 5 * index);

byte[] buffer = new byte[5];

isBundlx.read(buffer);

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;

FileInputStream isBundle = new FileInputStream(bundleFileName);

isBundle.skip(offset);

byte[] lengthBytes = new byte[4];

isBundle.read(lengthBytes);

int length = (int) (lengthBytes[0] & 0xff)

+ (int) (lengthBytes[1] & 0xff) * 256

+ (int) (lengthBytes[2] & 0xff) * 65536

+ (int) (lengthBytes[3] & 0xff) * 16777216;

result = new byte[length];

isBundle.read(result);

然后通过Flex API写了一个自定义的切片图层,重写了getTileURL方法:

override protected function getTileURL(level:Number, row:Number,col:Number):URLRequest

{

var url:String = "http://localhost:8777/restserver/tile/"+level+"/"+row+"/"+col;

return new URLRequest(url);

}

看个效果吧,算是对上述分析的验证:

clip_image003


http://help.arcgis.com/en/arcgisserver/10.0/help/arcgis_server_dotnet_help/index.html#//00930000165z000000.htm

相关文章:

  • CS224d Problem set 2作业
  • 一个日志输出系统的设计
  • linux内核内存管理中的pagevec结构体
  • poj_2352 线段树
  • Mac周边环境 goBASIC语言HelloWorld
  • linux内存管理系统后期的内核对zonelist的简化
  • bzoj3809: Gty的二逼妹子序列
  • linux内核page结构体的PG_referenced和PG_active标志
  • 解決BufferedReader读取UTF-8文件中文乱码(转)
  • 问题以及发现问题和解决问题
  • bitmap格式分析(转)
  • 关于数组或集合中判断存在某个元素
  • kexec机制
  • Spring事务配置的五种方式
  • buffer_head和bio
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • Angular数据绑定机制
  • angular学习第一篇-----环境搭建
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • PAT A1092
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • redis学习笔记(三):列表、集合、有序集合
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 安卓应用性能调试和优化经验分享
  • 从tcpdump抓包看TCP/IP协议
  • 分布式熔断降级平台aegis
  • 基于遗传算法的优化问题求解
  • 简单易用的leetcode开发测试工具(npm)
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 如何实现 font-size 的响应式
  • 使用Swoole加速Laravel(正式环境中)
  • 学习JavaScript数据结构与算法 — 树
  • 一份游戏开发学习路线
  • 浅谈sql中的in与not in,exists与not exists的区别
  • 数据可视化之下发图实践
  • 正则表达式-基础知识Review
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​卜东波研究员:高观点下的少儿计算思维
  • #pragam once 和 #ifndef 预编译头
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • $.ajax()参数及用法
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (AtCoder Beginner Contest 340) -- F - S = 1 -- 题解
  • (floyd+补集) poj 3275
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (Ruby)Ubuntu12.04安装Rails环境
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (第二周)效能测试
  • (二)hibernate配置管理
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (原)记一次CentOS7 磁盘空间大小异常的解决过程
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .net CHARTING图表控件下载地址