《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);
时间: 2024-08-19 06:26:06

《ArcGIS Runtime SDK for Android开发笔记》——(12)、自定义方式加载Bundle格式缓存数据的相关文章

《ArcGIS Runtime SDK for Android开发笔记》——(10)、ArcGIS Runtime SDK支持的空间数据类型

1.前言 移动端的数据来源非常重要,它决定了移动端功能的实现.早期的ArcGIS Android API中,主要以接入在线的数据源为主,因此主要实现在线的地图浏览.查询和路径分析.地理处理等从操作:在v1.0.1版本中,ArcGIS移动产品第一次可以加载松散型切片,自此逐渐掀开了对本地离线数据源的支持,也因此可以在移动端实现越来越受欢迎的离线功能.现在最新的10.2.7 API离线支持数据主要包括紧凑型切片.tpk切片包..geodatabase..shp文件.地名地址库.网络数据集. 转载请注

《ArcGIS Runtime SDK for Android开发笔记》——离在线一体化技术:概述

1.前言 数据生产和数据展示是常见的两大专业级移动GIS应用场景,这里我们针对数据生产环节的ArcGIS的离在线一体化技术给大家做一个基本的介绍和梳理. 使用ArcGIS离在线一体化技术首先需要以下基础环境: ArcGIS for Desktop 10.2.1以上版本 ArcGIS for Server 10.2.1以上版本 使用PostgreSQL.Microsoft SQL Server.或 Oracle 设置企业级地理数据库ArcSDE. 再次在使用同步功能是必须给要素添加GlobleID

《ArcGIS Runtime SDK for Android开发笔记》——离在线一体化技术:离线矢量数据同步

1.前言 上一篇文章中我们实现了离线要素的编辑操作,这一篇中主要介绍离在线一体化技术中最后一个环节离线数据的同步功能,通过对数据的上传,服务器端的版本化管理,实现数据生产管理的整个流程. 转载请注明出处:http://www.cnblogs.com/gis-luq/p/5858062.html 2.demo实现过程 2.1.Demo UI实现 activity_main.xml <?xml version="1.0" encoding="utf-8"?>

《ArcGIS Runtime SDK for Android开发笔记》

开发笔记之基础教程 ArcGIS Runtime SDK for Android 各版本下载地址 <ArcGIS Runtime SDK for Android开发笔记>——(1).Android Studio下载与安装 <ArcGIS Runtime SDK for Android开发笔记>——(2).Android Studio基本配置与使用 <ArcGIS Runtime SDK for Android开发笔记>——(3).ArcGIS Runtime SDK概述

《ArcGIS Runtime SDK for Android开发笔记》——(9)、空间数据的容器-地图MapView

1.前言 在上一篇内容里介绍了 关于ArcGIS Android开发的未来(“Quartz”版Beta)相关内容,期间也提到了关于API接口的重构,开发思路的调整,根据2015UC资料也可以知道新版预计将在明年的时候推出.届时在开发思路上将会往新版迁移. 总的来说,虽然“Quartz”版的开发思路有所变化,但总体变化不大,这里我将继续以现有正式发布版本为主梳理ArcGIS Runtime SDK for Android 开发内容. 参考API版本号:version 10.2.7.后续内容若不做特

《ArcGIS Runtime SDK for Android开发笔记》——基于Android Studio构建ArcGIS Android开发环境(离线部署)

1.前言 在上一篇的内容里我们介绍了基于Android Studio构建ArcGIS Runtime SDK for Android开发环境的基本流程,流程中我们采用的是基于Gradle的构建方式,在这种方式里主要通过设置maven仓库位置,设置编译选项.依赖版本在联网环境下下载对应SDK依赖包. 但是在网络情况不好的情况下这种方式就不行了,那怎么解决在离线环境或者内网环境实现开发流程呢?目前了解到的有两种方式: 1)配置内网环境maven仓库,预先缓存到内网环境下,然后离线调用. 2)采用直接

《ArcGIS Runtime SDK for Android开发笔记》——ArcGIS Runtime SDK概述

1.前言 ArcGIS Runtime SDK是一整套用于构建原生及跨平台的应用程序开发包, 转载请注明出处:http://www.cnblogs.com/gis-luq/p/4765993.html 2.ArcGIS Runtime SDKs产品家族 相信大部分开发者对ArcGIS Runtime SDKs这个名称并不生疏,它实际上包含了一系列SDKs,用来开发应用于桌面和移动设备的应用程序.在10.2.2之前的版本中,ArcGIS Runtime SDKs包括ArcGIS Runtime S

《ArcGIS Runtime SDK for Android开发笔记》——(15)、要素绘制Drawtools3.0工具DEMO

1.前言 移动GIS项目开发中点线面的要素绘制及编辑是最常用的操作,在ArcGIS Runtime SDK for iOS 自带AGSSketchLayer类可以帮助用户快速实现要素的绘制,图形编辑.但是在ArcGIS Runtime SDK for Android的版本中并没有提供类似的功能,实现过程相对较复杂.(10.2.8及以下版本需要用户自定义扩展实现,通过扩展MapOnTouchListener类实现,Quartz版SDK默认自带) 之前有大神gispace封装了DrawTools2.

《ArcGIS Runtime SDK for Android开发笔记》——(8)、关于ArcGIS Runtime SDK for Android开发的一些思考

1.前言 今天再一次在官网看到了ArcGIS Runtime SDK for Android下一个版本“Quartz”版的更新资料,它将是一个非常重要的更新,包括API接口的重构和开发思路的调整.具体如下图所示: 在ArcGIS Runtime SDK概述一文中已经给大家介绍了ArcGIS Runtime SDK for Android的前世今生,从2011年6月发布ArcGIS Runtime SDK for Android beta版开始,到2015年10月1日发布ArcGIS Runtim