《ArcGIS Runtime SDK for Android开发笔记》——(12)、自定义方式加载Bundle格式缓存数据
随着ArcGIS 10.3的正式发布,Esri推出了新的紧凑型缓存格式以增强用户的访问体验。新的缓存格式下,Esri将缓存的索引信息.bundlx包含在了缓存的切片文件.bundle中。具体如下图所示:

对于bundle格式的具体解析,这里就不再详述,具体可以查阅8013是我的博文《ArcGIS for Server 10.3.X 新型紧凑型缓存的解读和应用》,本文内容就是根据其所述实现。再熟悉bundle实现机理后,结合相关加密算法,可以实现进一步缓存数据的加密解密过程。
转载请注明出处:http://www.cnblogs.com/gis-luq/p/5390343.html
以下仅列出Bundle格式数据的两种加载方式:
1、api默认加载方式
// Add Local tiled layer to MapView
ArcGISLocalTiledLayer agsLocaltiledlyr = new ArcGISLocalTiledLayer("file:///mnt/sdcard/ArcGIS/sample/HelloWorld/Layers");
map.addLayer(agsLocaltiledlyr);
2、利用图层扩展自定义加载Bundle
package com.gis_luq.bundleandroid; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log; import com.esri.android.map.TiledServiceLayer;
import com.esri.android.map.TiledServiceLayer.TileInfo;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference; import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; public class BundleLayer extends TiledServiceLayer { private String TAG = "BundleLayer"; private TileInfo tileInfo;
private SpatialReference spatialReference;
private Envelope fullExtent;
private String compactTileLoc; //web墨卡托默认值
// private double xmin = 8176078.237600003;
// private double ymin = 2056264.7502700;
// private double xmax = 15037808.29357646;
// private double ymax = 7087593.892070787;
// private Point origin = new Point(-20037508.342787001, 20037508.342787001);
// private double[] scale = new double[]{591657527.591555, 295828763.79577702, 147914381.89788899, 73957190.948944002, 36978595.474472001, 18489297.737236001, 9244648.8686180003,
// 4622324.4343090001,2311162.2171550002,1155581.108577,577790.55428899999,288895.27714399999,144447.638572,72223.819285999998,36111.909642999999,18055.954822,9027.9774109999998,
// 4513.9887049999998,2256.994353,1128.4971760000001};
// private double[] res = new double[]{156543.03392799999,78271.516963999893, 39135.758482000099, 19567.879240999901, 9783.9396204999593,4891.9698102499797, 2445.9849051249898, 1222.9924525624899, 611.49622628138002,
// 305.74811314055802,152.874056570411,76.437028285073197,38.218514142536598,19.109257071268299,9.5546285356341496,4.7773142679493699,2.38865713397468,
// 1.1943285668550501,0.59716428355981699,0.29858214164761698};
// private int levels = 20;
// private int dpi = 96;
// private int tileWidth = 256;
// private int tileHeight = 256; public BundleLayer(String compactTileLoc) {
super(compactTileLoc);
this.compactTileLoc = compactTileLoc;
this.initTileInfo(compactTileLoc);
this.initLayer();
} private void initTileInfo( String compactTileLoc){ //以下为需要从配置文件获取到的信息
int wkid = 102100,dpi =96,levels=20,tileCols=256,tileRows=256;
double xmin =8176078.237600003, ymin = 2056264.7502700, xmax = 15037808.29357646, ymax = 7087593.892070787;
double TileOrigin_x =-20037508.342787001 ,TileOrigin_y = 20037508.342787001;
List<LODInfo> lodInfoList = new ArrayList<>(); //初始化budle相关信息
String strConf = compactTileLoc + "/Conf.xml";
DocumentBuilderFactory factory=null;
DocumentBuilder builder=null;
Document document=null;
InputStream inputStream=null;
//首先找到xml文件
factory= DocumentBuilderFactory.newInstance();
try {
//找到xml,并加载文档
builder= factory.newDocumentBuilder();
File f = new File(strConf);
inputStream=new FileInputStream(f);
document=builder.parse(inputStream);
//找到根Element
Element root=document.getDocumentElement(); wkid =Integer.parseInt( root.getElementsByTagName("WKID").item(0).getChildNodes().item(0).getNodeValue());
dpi = Integer.parseInt( root.getElementsByTagName("DPI").item(0).getChildNodes().item(0).getNodeValue());
tileCols = Integer.parseInt( root.getElementsByTagName("TileCols").item(0).getChildNodes().item(0).getNodeValue());
tileRows = Integer.parseInt( root.getElementsByTagName("TileRows").item(0).getChildNodes().item(0).getNodeValue());
TileOrigin_x = Double.valueOf(root.getElementsByTagName("TileOrigin").item(0).getChildNodes().item(0).getChildNodes().item(0).getNodeValue());
TileOrigin_y = Double.valueOf(root.getElementsByTagName("TileOrigin").item(0).getChildNodes().item(1).getChildNodes().item(0).getNodeValue()); //LODInfos
NodeList nodes = root.getElementsByTagName("LODInfos").item(0).getChildNodes();
levels = nodes.getLength();
//遍历根节点所有子节点,rivers 下所有river
LODInfo lodInfo=null;
for(int i=0;i<nodes.getLength();i++){
lodInfo=new LODInfo();
//获取river元素节点
Element riverElement=(Element)(nodes.item(i));
lodInfo.LevelID =Integer.parseInt(riverElement.getChildNodes().item(0).getChildNodes().item(0).getNodeValue());
lodInfo.Scale = (Double.valueOf(riverElement.getChildNodes().item(1).getChildNodes().item(0).getNodeValue()));
lodInfo.Resolution = (Double.valueOf(riverElement.getChildNodes().item(2).getChildNodes().item(0).getNodeValue()));
Log.d(TAG,"LevelID:"+lodInfo.LevelID +" Scale:"+lodInfo.Scale + " Resolution:"+lodInfo.Resolution);
lodInfoList.add(lodInfo);
}
}catch (Exception e){
e.printStackTrace();
}finally{
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
} spatialReference = SpatialReference.create(wkid);
fullExtent = new Envelope(xmin, ymin, xmax, ymax);
setFullExtent(fullExtent);
Point origin = new Point(TileOrigin_x, TileOrigin_y); double[] scal = new double[lodInfoList.size()];
double[] resls = new double[lodInfoList.size()];
for (int i=0;i<lodInfoList.size();i++){
scal[i] = lodInfoList.get(i).Scale;
resls[i] = lodInfoList.get(i).Resolution;
}
tileInfo = new TileInfo(origin,scal ,resls, levels, dpi, tileCols, tileRows);
} protected void initLayer() {
if(getID()==0){
this.nativeHandle = create();
}
setFullExtent(fullExtent);
setTileInfo(tileInfo);
setDefaultSpatialReference(spatialReference);
super.initLayer();
} @Override
public TileInfo getTileInfo() {
return this.tileInfo;
} @Override
public Envelope getFullExtent() {
return this.fullExtent;
} @Override
public SpatialReference getSpatialReference() {
return this.spatialReference;
} @Override
protected byte[] getTile(int mLevel, int mColumn, int mRow) throws Exception { //第一步,根据参数中的比例级别、列号和行号定位到Bundle文件。
String level = Integer.toString(mLevel);
int levelLength = level.length();
if(levelLength == 1){
level = "0" + level;
}
level = "L" + level; int rowGroup = 128*(mRow/128);
String row = Integer.toHexString(rowGroup);
int rowLength = row.length();
if(rowLength < 4){
for(int i=0; i<4-rowLength; i++){
row = "0" + row;
}
}
row = "R" + row; int columnGroup = 128*(mColumn/128);
String column = Integer.toHexString(columnGroup);
int columnLength = column.length();
if(columnLength < 4) {
for(int i=0; i<4-columnLength; i++){
column = "0" + column;
}
}
column = "C" + column; String bundleFileName = String.format("%s/%s/%s%s", compactTileLoc+"/_alllayers", level, row, column) + ".bundle"; //第二步,读取bundle文件,根据前面分析中所推断出的切片的起始位置和切片的长度获取对应的切片并返回
int index = 128*(mRow - rowGroup) + (mColumn-columnGroup); RandomAccessFile isBundle = new RandomAccessFile(bundleFileName, "r");
isBundle.skipBytes(64 + 8*index); //获取位置索引并计算切片位置偏移量
byte[] indexBytes = new byte[4];
isBundle.read(indexBytes, 0, 4);
long offset = (long)(indexBytes[0]&0xff) +(long)(indexBytes[1]&0xff)*256 + (long)(indexBytes[2]&0xff)*65536
+ (long)(indexBytes[3]&0xff)*16777216; //获取切片长度索引并计算切片长度
long startOffset = offset - 4;
isBundle.seek(startOffset);
byte[] lengthBytes = new byte[4];
isBundle.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; //根据切片位置和切片长度获取切片
ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] tileBytes = new byte[length];
int bytesRead = 0;
if(length > 0){
bytesRead = isBundle.read(tileBytes, 0, tileBytes.length);
if(bytesRead > 0){
bos.write(tileBytes, 0, bytesRead);
}
} byte[] tile = bos.toByteArray();
return tile;
} public void saveBitmap(String picName, Bitmap bm) {
System.out.println("保存图片");
File f = new File(Environment.getExternalStorageDirectory() + "/arcgis", picName);
if (f.exists()) {
f.delete();
}
try {
FileOutputStream out = new FileOutputStream(f);
bm.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
System.out.println("已经保存");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } public class LODInfo{
public int LevelID;
public double Scale;
public double Resolution;
} }
使用方法:
//默认bundle数据读取方式
String localUrl= Environment.getExternalStorageDirectory().getPath() +"/bundle/Layers";
ArcGISLocalTiledLayer arcGISLocalTiledLayer = new ArcGISLocalTiledLayer(localUrl);
this.mapView.addLayer(arcGISLocalTiledLayer); //自定义bundle数据读取
String local= Environment.getExternalStorageDirectory().getPath() +"/bundle/Layers";
BundleLayer bundleLayer = new BundleLayer(local);
this.mapView.addLayer(bundleLayer);
《ArcGIS Runtime SDK for Android开发笔记》——(12)、自定义方式加载Bundle格式缓存数据的更多相关文章
- 《ArcGIS Runtime SDK for Android开发笔记》
开发笔记之基础教程 ArcGIS Runtime SDK for Android 各版本下载地址 <ArcGIS Runtime SDK for Android开发笔记>——(1).And ...
- 《ArcGIS Runtime SDK for Android开发笔记》——离在线一体化技术:概述
1.前言 数据生产和数据展示是常见的两大专业级移动GIS应用场景,这里我们针对数据生产环节的ArcGIS的离在线一体化技术给大家做一个基本的介绍和梳理. 使用ArcGIS离在线一体化技术首先需要以下基 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——离在线一体化技术:离线矢量数据同步
1.前言 上一篇文章中我们实现了离线要素的编辑操作,这一篇中主要介绍离在线一体化技术中最后一个环节离线数据的同步功能,通过对数据的上传,服务器端的版本化管理,实现数据生产管理的整个流程. 转载请注明出 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(7)、示例代码arcgis-runtime-samples-android的使用
1.前言 学习ArcGIS Runtime SDK开发,其实最推荐的学习方式是直接看官方的教程.示例代码和帮助文档,因为官方的示例一般来说都是目前技术最新,也是最详尽的.对于ArcGIS Runtim ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(11)、ArcGIS Runtime SDK常见空间数据加载
ArcGIS Runtime SDK for Android 支持多种类型空间数据源.每一种都提供了相应的图层来直接加载,图层Layer是空间数据的载体,其主要继承关系及类型说明如下图所示: 转载请注 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(5)、基于Android Studio构建ArcGIS Android开发环境(离线部署)(转)
1.前言 在上一篇的内容里我们介绍了基于Android Studio构建ArcGIS Runtime SDK for Android开发环境的基本流程,流程中我们采用的是基于Gradle的构建方式,在 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(6)、基于Android Studio的ArcGIS Android工程结构解析
1.前言 Android Studio 是第一个Google官方的 Android 开发环境.其他工具,例如 Eclipse,在 Android Studio 发布之前已经有了大规模的使用.为了帮助开 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(9)、空间数据的容器-地图MapView
1.前言 在上一篇内容里介绍了 关于ArcGIS Android开发的未来(“Quartz”版Beta)相关内容,期间也提到了关于API接口的重构,开发思路的调整,根据2015UC资料也可以知道新版预 ...
- 《ArcGIS Runtime SDK for Android开发笔记》——(3)、ArcGIS Runtime SDK概述
1.前言 ArcGIS Runtime SDK是一整套用于构建原生及跨平台的地图应用程序的开发包,包括移动设备的Android.iOS.Windows Phone,针对桌面的.Net.Java.OSX ...
随机推荐
- github提交项目
1.建立git仓库 在GitHub上先新建一个仓库. 新建一个文件夹 cd git clone https://github.com/xxxxxxx/xxxxx.git 3.把你的项目拖进去,cd 进 ...
- C++_新特性1-类型转换运算符
C++的创始人认为C语言的类型转换运算符太过于松散.他采取了更加严格的限制允许的类型转换.并添加了4个类型转换运算符. 这部分特性比较高阶,我把它归于奇技淫巧的范畴.这里简单介绍一下,以后实际有用到再 ...
- Webstorm和 Eclipise 快捷键,慢慢总结下。
Eclipise: 查找代码: ctrl + H 快速选择一行: shift + 下/shift + 上 到指定行: ctrl + L Webstorm: 查找 ...
- lambda 表达式定制操作
泛型算法中的定制操作 许多算法都会比较输入序列中的元素以达到排序的效果,通过定制比较操作,可以控制算法按照编程者的意图工作. 普通排序算法: template<class RandomItera ...
- [转] 一个简单的零配置命令行HTTP服务器 - http-server (nodeJs)
[From] http://www.cnblogs.com/lucker/p/4108838.html http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs. 如果你不 ...
- PIE SDK缨帽变换
1.算法功能简介 缨帽变换是根据多光谱遥感中土壤.植被等信息在多维光谱空间中信息分布结构对图像做的经验性线性正交变换. PIE 支持对 Landsat MSS. Landsat 5 TM.Landsa ...
- nginx 配置优化指令
worker_processes worker_processes指令是用来设计Nginx进程数,官方默认设为1,赋值太多了,将会对系统IO影响效率,降低Nginx服务器性能.但是为了让多核CPU能够 ...
- gulp优化hexo方法
gulp通过对站点使用的静态资源进行压缩,来优化网站的访问速度. 首先安装gulp以及所需要的模块: npm install gulp -g npm install gulp-htmlclean gu ...
- 牛客网Java刷题知识点之OSI七层参考模型 和 TCP/IP五层参考模型
不多说,直接上干货! 福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号: 大数据躺过的坑 Java从入门到架构师 人工智能躺过的坑 ...
- 基于前端js模板替换的多语言方案思考
最近在做将一个系统多语言化的项目,系统使用的是ASP.NET,直接使用了一种已有的方案:在页面渲染时采用正则表达式替换{XXX:001 确定}格式的标记.但是这个方式增加了服务端的字符串处理,对页面性 ...