1. 程式人生 > >Arcgis for android100.3 載入影像地圖並且快取地圖

Arcgis for android100.3 載入影像地圖並且快取地圖

這裡載入並且快取影像的目的是為了防止遙感影像的拷貝,專案之前都是在拷貝影像(因為資料較大,下載太費事),在此進行更新,改為先是載入線上影像,然後再進行快取。

一、先重寫了Arcgis for android的一個ImageTiledLayer(因為別的大多數方法是不允許被重寫的----),先看一下官方文件的介紹:

大致可以看出來,我們可以通過重寫這個類的方法來獲得給定切片鍵的切片的編碼位元組,即獲得釋出影像的切片資訊。

下面我們看一下這裡面的一個方法:

我們就是要重寫這個方法!

在寫之前我們先看一下發布的影像服務:

這些引數待會都是我們需要用到的。

下面給大家看一下重寫的ImageTileLayer:程式碼如下:

public class ArcGISLocalDataLayer extends ImageTiledLayer {
    private String mUrl = null;
    private Context mContext;
    private SQLiteDatabase mDb;

    //  private SpatialReference mSpatialReference;
    public ArcGISLocalDataLayer(Context mContext, String url, TileInfo tileInfo, Envelope envelope) {
        super(tileInfo, envelope);
        this.mContext = mContext;
        this.mUrl = url;
        this.init();
    }

    private void init() {

        DBHelper gadbHelper = new DBHelper(mContext);
        mDb = gadbHelper.getWritableDatabase();
        //setDefaultSpatialReference(SpatialReference.create(4326));
        //設定mapservice
        //ArcGISLocalDataLayer.this.initLayer();
    }


    @Override
    protected byte[] getTile(TileKey tileKey) {
        int level = tileKey.getLevel();
        int col = tileKey.getColumn();
        int row = tileKey.getRow();
        byte[] result = null;
        String tileIndex = "tile_" + level + "_" + row + "_" + col;
        String sql = "select * from " + DBHelper.TABLE_MAP + " where TILEINDEX = '" + tileIndex + "'";
        Cursor mCursor = mDb.rawQuery(sql, null);
        boolean hasData = false;
        boolean isWifi = isWifi();
        while (mCursor.moveToNext()) {//判斷是否存在資料
            hasData = true;
        }
        if (hasData && !isWifi) {//資料庫中有資料
            // super.getTile(tileKey);
            try {
                if (mCursor.moveToFirst()) {
                    String path = mCursor.getString(mCursor.getColumnIndex("TILEDATA"));
                    result = PictureUtil.getFileByte(path);
                }
                mCursor.close();
            } catch (Exception e) {
                e.printStackTrace();
                result = downloadData(tileIndex);
            }
        } else {//資料庫中沒有資料
            result = downloadData(tileIndex);
        }
        return result;
    }

    //線上載入
    private byte[] downloadData(String tileIndex) {
        byte[] result = null;
        try {
            URL url = new URL(mUrl + "/" + tileIndex.replaceAll("_", "/"));
            System.out.println("載入地圖時的切片URL》》》》》:" + url.toString());
            byte[] buf = new byte[1024];
            HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.connect();
            BufferedInputStream is = new BufferedInputStream(httpConnection.getInputStream());
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int temp = -1;
            while ((temp = is.read(buf)) > 0) {
                bos.write(buf, 0, temp);
            }
            is.close();
            httpConnection.disconnect();
            result = bos.toByteArray();
            insertOrUpdateToDb(tileIndex, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private boolean isWifi() {
        ConnectivityManager connectivityManager = (ConnectivityManager) mContext
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null
                && activeNetInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            return true;
        }
        return false;
    }

    //新增資料
    private void insertOrUpdateToDb(String tileIndex, byte[] result) {
        ContentValues values = new ContentValues();
        String path = Environment.getExternalStorageDirectory() + "/agisdata/tiles/" + tileIndex + ".png";
        values.put("TILEINDEX", tileIndex);
        values.put("TILEDATA", path);
        values.put("TILETIME", System.currentTimeMillis() + "");

        //首選判斷資料庫表中檔案大小時候超過四百兆,如果超過,就進行一次刪除
        File file = new File("/data/data/com.zx.gamarketmobile/databases/" + DBHelper.TABLE_MAP);
        Cursor cs = mDb.rawQuery("select * from " + DBHelper.TABLE_MAP, null);
        int count = cs.getCount();
        cs.close();
        if (getFileSize(file) > 300) {//當快取大於300兆時,刪除資料庫中前百分之三十的資料
            int deleteLimit = (int) (count * 0.3);
            String sqlDelete = "delete from " + DBHelper.TABLE_MAP + " where TILEINDEX in ( select TILEINDEX from " + DBHelper.TABLE_MAP + " order by TILETIME desc limit 0," + deleteLimit + " )";
            mDb.execSQL(sqlDelete);
        }

        String sql = "select * from " + DBHelper.TABLE_MAP + " where TILEINDEX = '" + tileIndex + "'";
        Cursor cursor = mDb.rawQuery(sql, null);
        boolean hasData = false;
        while (cursor.moveToNext()) {
            hasData = true;
        }
        cursor.close();
        if (hasData) {
            mDb.update(DBHelper.TABLE_MAP, values, "TILEINDEX = ?", new String[]{tileIndex});
        } else {
            mDb.insert(DBHelper.TABLE_MAP, "value", values);
        }
        PictureUtil.saveFileWithByte(result, path);
    }

    //獲取檔案大小
    private double getFileSize(File file) {
        DecimalFormat df = new DecimalFormat("#.00");
        double size = 0;
        try {
            if (file.exists()) {
                FileInputStream fIs = new FileInputStream(file);
                size = Double.valueOf(df.format((double) fIs.available() / 1048576));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return size;
    }

}

這裡面有一部分資料庫的操作,但是後來並沒有使用資料庫,所以有關於資料庫的程式碼實際上是不起作用的。考慮到釋放快取問題,我直接把影像切片放到本地的資料夾Tile裡面。

程式碼裡面要說的就是getTile方法裡面的三個引數,這是快取的重點:

int level = tileKey.getLevel();
int col = tileKey.getColumn();
int row = tileKey.getRow();

這三個引數實際上是跟釋出的影像服務的切片等級和行列相對應的,大家再看一下發布的影像的切片結構:

可以看到,這些切片先是分不同的等級,然後每一個等級還分為不同的行列。

getTile的引數就是來那這些level、col、和row的。

二、開始使用我們重寫的方法

1、首先先例項化一個ArcGISLocalDataLayer,程式碼如下:

 //引數:1、上下文環境  2、服務的url  3、tileinfo  4、影像envelop  5、空間參考系
        final ArcGISLocalDataLayer online=new ArcGISLocalDataLayer(this,
                "http://123.206.81.238:6080/arcgis/rest/services/WaiYeHeCha/" +
                        "qingdaoImage2000/MapServer",info,new Envelope(4.04291844967241E7,
                3893593.058001068,4.0596572781676605E7,4129366.8738666903,SpatialReference.create(4528)));

這段程式碼相信大家都能看明白,註釋也很清楚。

2、看一下上面程式碼中的info,這是例項化的一個TileInfo,下面我們根據影像把它寫出來:

   //tileinfo引數:1、dpi  2、影象格式  3、切片分層  4、原點(origin)
        //5、空間參考wkid  6、高度  7、寬度
        TileInfo info=new TileInfo(96, TileInfo.ImageFormat.PNG24,
                list,point,SpatialReference.create(4528),256,256);

這些引數怎麼來的呢?看一下發布的影像服務的資料:

我們需要的引數這裡面全都有!切片分層我單獨列出來了,如下:

   List<LevelOfDetail> list=new ArrayList<>();
        final LevelOfDetail one=new LevelOfDetail(0,529.1677250021168,2000000);
        LevelOfDetail two=new LevelOfDetail(1,264.5838625010584,1000000);
        LevelOfDetail three=new LevelOfDetail(2,132.2919312505292,500000);
        LevelOfDetail four=new LevelOfDetail(3,66.1459656252646,250000);
        LevelOfDetail five=new LevelOfDetail(4,33.0729828126323,125000);
        LevelOfDetail six=new LevelOfDetail(5,16.933367200067735,64000);
        LevelOfDetail seven=new LevelOfDetail(6,8.466683600033868,32000);
        LevelOfDetail eight=new LevelOfDetail(7,4.233341800016934,16000);
        LevelOfDetail nine=new LevelOfDetail(8,2.116670900008467,8000);
        list.add(one);
        list.add(two);
        list.add(three);
        list.add(four);
        list.add(five);
        list.add(six);
        list.add(seven);
        list.add(eight);
        list.add(nine);

下面我們執行載入影像的操作即可!

我把這一部分的程式碼放在這兒:

  List<LevelOfDetail> list=new ArrayList<>();
        final LevelOfDetail one=new LevelOfDetail(0,529.1677250021168,2000000);
        LevelOfDetail two=new LevelOfDetail(1,264.5838625010584,1000000);
        LevelOfDetail three=new LevelOfDetail(2,132.2919312505292,500000);
        LevelOfDetail four=new LevelOfDetail(3,66.1459656252646,250000);
        LevelOfDetail five=new LevelOfDetail(4,33.0729828126323,125000);
        LevelOfDetail six=new LevelOfDetail(5,16.933367200067735,64000);
        LevelOfDetail seven=new LevelOfDetail(6,8.466683600033868,32000);
        LevelOfDetail eight=new LevelOfDetail(7,4.233341800016934,16000);
        LevelOfDetail nine=new LevelOfDetail(8,2.116670900008467,8000);
        list.add(one);
        list.add(two);
        list.add(three);
        list.add(four);
        list.add(five);
        list.add(six);
        list.add(seven);
        list.add(eight);
        list.add(nine);
        //origin點座標和空間參考系wkid
        Point point=new Point(3.48768E7 ,1.00021E7,SpatialReference.create(4528));
        //tileinfo引數:1、dpi  2、影象格式  3、切片分層  4、原點(origin)
        //5、空間參考wkid  6、高度  7、寬度
        TileInfo info=new TileInfo(96, TileInfo.ImageFormat.PNG24,
                list,point,SpatialReference.create(4528),256,256);

        //引數:1、上下文環境  2、服務的url  3、tileinfo  4、影像envelop  5、空間參考系
        final ArcGISLocalDataLayer online=new ArcGISLocalDataLayer(this,
                "http://123.206.81.238:6080/arcgis/rest/services/WaiYeHeCha/" +
                        "qingdaoImage2000/MapServer",info,new Envelope(4.04291844967241E7,
                3893593.058001068,4.0596572781676605E7,4129366.8738666903,SpatialReference.create(4528)));
       // Envelope size=CalculateUtil.getColRow(point,new Envelope(4.04291844967241E7,
               // 3893593.058001068,4.0596572781676605E7,4129366.8738666903,SpatialReference.create(4528)),one.getResolution(),256);
        //Envelope size2=CalculateUtil.getColRow(point,new Envelope(4.04291844967241E7,
               // 3893593.058001068,4.0596572781676605E7,4129366.8738666903,SpatialReference.create(4528)),eight.getResolution(),256);

//        opencaheMap= (CheckBox) LayoutInflater.from(MainActivity.this).inflate(R.layout.pop_layers,null).findViewById(R.id.ck_selectcahe);
        map.getOperationalLayers().add(online);

最終效果是在瀏覽地圖的時候,直接把瀏覽過的地圖快取到了本地!