这里加载并且缓存影像的目的是为了防止遥感影像的拷贝,项目之前都是在拷贝影像(因为数据较大,下载太费事),在此进行更新,改为先是加载在线影像,然后再进行缓存。
一、先重写了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);
最终效果是在浏览地图的时候,直接把浏览过的地图缓存到了本地!