ArcGIS Runtime 100.2.0的正式发布带来个更多移动端的处理地图的能力,例如支持WMS图层、支持海图(ENC)图层,再如基于场景相机(Camera)的视域分析。同时还提供了新的统计查询API(总和,平均值,计数,最小值,最大值,标准差或方差)。而今天要描述的重点是ArcGIS Runtime 100.2.0对Shapefile数据格式的支持,当然ArcGIS Runtime 100.2.0还增加了对OGC GeoPackage格式的支持。
对于Shapfile这一文件格式,ArcGIS Runtime SDK for Android 在100.2.0系列中推出了两大重磅利好消息:
(1) 实现对本地 Shapfile 文件的加载、显示和查询
(2) 实现对本地 Shapefile 文件的编辑,即增删改操作
毋庸置疑,对于广大的ArcGIS移动端爱好者而言,支持Shapefile编辑操作绝对是一则振奋人心的好消息。
Shapefile文件的加载
加载shp文件,数据量大的时候,加载速度比较慢。
shp文件 必须有least three files (.shp, .shx, .dbf) 如果有prj文件那就更好。位置准确。
在安卓端加载Shapefile文件的关键是ShapefileFeatureTable(com.esri.arcgisruntime.data.ShapefileFeatureTable)。
相比于.geodatabase文件,Shapefile文件的缺点在于只是单图层,且没有符号化,当然可以通过移动端的可视化API进行处理。
shp文件加载方式
/**
* 加载shp文件 - 方式一
* */
public void loadShpFileOne(){
// shp文件存在本地的路径
String shpPath = context.getExternalFilesDir("/shp").getPath()+ File.separator + "地名检查.shp";
shapefileFeatureTable = new ShapefileFeatureTable(shpPath);
shapefileFeatureTable.loadAsync();
// 构建featureLayerr
featureLayer = new FeatureLayer(shapefileFeatureTable);
// 设置Shapefile文件的渲染方式
SimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.GREEN, 3);
// 渲染线和面
// SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 1.0f);
// SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
// SimpleRenderer renderer = new SimpleRenderer(fillSymbol);
// 仅仅渲染点
SimpleRenderer renderer = new SimpleRenderer(simpleMarkerSymbol);
featureLayer.setRenderer(renderer);
//标注shp
String strLabelDefinition = "{\"labelExpression\": \"[NAME]\",\"labelPlacement\": \"esriServerPointLabelPlacementAboveCenter\",\"symbol\": {\"color\": [255,100,0,255],\"font\": {\"size\": 6,\"family\":\"Microsoft YaHei\"},\"type\": \"esriTS\"}}";
LabelDefinition labelDefinition = LabelDefinition.fromJson(strLabelDefinition);
featureLayer.getLabelDefinitions().add(labelDefinition);
featureLayer.setLabelsEnabled(true);
// 设置渲染图层添加到mapView
mapView.getMap().getOperationalLayers().add(featureLayer);
// 设置Callout
mCallout = mapView.getCallout();
createCallout();
}
/**
* 加载shp文件 - 方式二
* */
public void loadShpFileTwo(){
// shp文件存在本地的路径
String shpPath = context.getExternalFilesDir("/shp").getPath()+ File.separator + "地名检查.shp";
//String shpPath = context.getExternalFilesDir("/shp").getPath()+ File.separator + "检查标注.shp";
mapView.setAttributionTextVisible(false);
File shpFile = new File(shpPath);
if (shpFile.exists()){// 判断是否存储这个文件
shapefileFeatureTable = new ShapefileFeatureTable(shpPath);
shapefileFeatureTable.loadAsync();
shapefileFeatureTable.addDoneLoadingListener(new Runnable() {
@Override
public void run() {
GeometryType geoType = shapefileFeatureTable.getGeometryType();
String name = shapefileFeatureTable.getTableName();
featureLayer = new FeatureLayer(shapefileFeatureTable);
if (featureLayer.getFullExtent() != null){
mapView.setViewpointGeometryAsync(featureLayer.getFullExtent());
}else {
featureLayer.addDoneLoadingListener(new Runnable() {
@Override
public void run() {
mapView.setViewpointGeometryAsync(featureLayer.getFullExtent());
}
});
}
// 设置渲染图层添加到mapView
mapView.getMap().getOperationalLayers().add(featureLayer);
}
});
}else {
// 创建
shpFile.mkdirs();
}
// 设置Shapefile文件的渲染方式
// 点
SimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.GREEN, 5);
// 线
// SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 1.0f);
// 面
// SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
// 渲染线和面
// SimpleRenderer renderer = new SimpleRenderer(fillSymbol);
SimpleRenderer renderer = new SimpleRenderer(simpleMarkerSymbol);
featureLayer.setRenderer(renderer);
}
渲染加载自定义图片:
// 获取资源中图片
BitmapDrawable startDrawable = (BitmapDrawable) ContextCompat.getDrawable(context,picRes);
// PictureMarkerSymbol
final PictureMarkerSymbol pinSourceSymbol;
try {
pinSourceSymbol = PictureMarkerSymbol.createAsync(startDrawable).get();
pinSourceSymbol.loadAsync();
pinSourceSymbol.addDoneLoadingListener(new Runnable() {
@Override
public void run() {
//添加新图形
Envelope point = featureLayer.getFullExtent();
Graphic pinSourceGraphic = new Graphic(point, pinSourceSymbol);
graphicsOverlay.getGraphics().add(pinSourceGraphic);
mMapView.getGraphicsOverlays().add(graphicsOverlay);
}
});
// 设置在点的上面偏移
pinSourceSymbol.setOffsetY(20);
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
上面的渲染方式不好用可以用下面的渲染方式:
GraphicsOverlay graphicsOverlay = new GraphicsOverlay();
// 设置Shapefile文件的渲染方式
// 点
SimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.RED, 20);
/*//线
SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 1.0f);
//面
SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
//渲染线和面
SimpleRenderer renderer = new SimpleRenderer(fillSymbol);*/
/* //显示shp标注
String strLabelDefinition = "{\"labelExpression\": \"[NAME]\",\"labelPlacement\": \"esriServerPointLabelPlacementAboveCenter\",\"symbol\": {\"color\": [255,100,0,255],\"font\": {\"size\": 6,\"family\":\"Microsoft YaHei\"},\"type\": \"esriTS\"}}";
LabelDefinition labelDefinition = LabelDefinition.fromJson(strLabelDefinition);
featureLayer.getLabelDefinitions().add(labelDefinition);
featureLayer.setLabelsEnabled(true);*/
Graphic pointGraphic = new Graphic(featureLayer.getFullExtent(),simpleMarkerSymbol);
graphicsOverlay.getGraphics().add(pointGraphic);
mMapView.getGraphicsOverlays().add(graphicsOverlay);
新增shp要素数据
/**
* 增加shp要素数据
*/
public void addShp() {
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Point mapPoint = mapView.screenToLocation(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())));
// 调用isEditable和canAdd方法判断文件是否支持编辑操作,是否可添加要素;否,则抛出信息
if (shapefileFeatureTable.isEditable() && shapefileFeatureTable.canAdd()) {
// 构建待增加的Feature对象,设置几何,设置属性
Feature feature = shapefileFeatureTable.createFeature();
feature.setGeometry(mapPoint);
//feature.getAttributes().put("NAME", "测试点");
// 调用addFeatureAsync方法增加要素
final ListenableFuture<Void> addFeatureOper = shapefileFeatureTable.addFeatureAsync(feature);
// 在操作完成的监听事件中判断操作是否成功
addFeatureOper.addDoneListener(new Runnable() {
@Override
public void run() {
try {
addFeatureOper.get();
if (addFeatureOper.isDone()) {
Toast.makeText(context, "数据添加成功",Toast.LENGTH_SHORT).show();
}
} catch (InterruptedException interruptedExceptionException) {
// 处理异常
} catch (ExecutionException executionException) {
// 处理异常
}
}
});
} else {
Toast.makeText(context, "shp数据不可被编辑",Toast.LENGTH_SHORT).show();
}
return super.onSingleTapConfirmed(e);
}
// 控制旋转
@Override
public boolean onRotate(MotionEvent event,double rotationAngle) {
return false;
}
});
}
删除shp要素数据
/**
* 删除shp要素数据
* */
public void delShp(){
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
android.graphics.Point clickPoint = new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY()));
// identify the GeoElements in the given layer
final ListenableFuture<IdentifyLayerResult> futureIdentifyLayer = mapView
.identifyLayerAsync(shapefileFeatureTable.getFeatureLayer(), clickPoint, 10, false, 1);
futureIdentifyLayer.addDoneListener(new Runnable() {
@Override
public void run() {
try {
// call get on the future to get the result
IdentifyLayerResult layerResult = futureIdentifyLayer.get();
List<GeoElement> resultGeoElements = layerResult.getElements();
if (!resultGeoElements.isEmpty()) {
for (Object element : resultGeoElements) {
if (element instanceof Feature) {
Feature feature = (Feature) element;
// 调用delFeatureAsync方法删除要素
final ListenableFuture<Void> delFeatureOper = shapefileFeatureTable.deleteFeatureAsync(feature);
delFeatureOper.addDoneListener(new Runnable() {
@Override
public void run() {
try {
delFeatureOper.get();
if (delFeatureOper.isDone()) {
Toast.makeText(context, "数据删除成功",Toast.LENGTH_SHORT).show();
}
} catch (InterruptedException interruptedExceptionException) {
// 处理异常
} catch (ExecutionException executionException) {
// 处理异常
}
}
});
}
}
}
} catch (Exception e1) {
Toast.makeText(context, "选择失败!",Toast.LENGTH_SHORT).show();
}
}
});
return super.onSingleTapConfirmed(e);
}
// 控制旋转
@Override
public boolean onRotate(MotionEvent event,double rotationAngle) {
return false;
}
});
}
修改shp要素数据
/**
* 查询并修改shp要素数据 --- 第一种方法
* */
public void updataShp(){
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
android.graphics.Point mTapPoint = new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY()));
mSelectedFeatures = new ArrayList<>();
// identify the GeoElements in the given layer
final ListenableFuture<IdentifyLayerResult> futureIdentifyLayer = mapView
.identifyLayerAsync(shapefileFeatureTable.getFeatureLayer(), mTapPoint, 10, false, 1);
futureIdentifyLayer.addDoneListener(new Runnable() {
@Override
public void run() {
try {
// call get on the future to get the result
IdentifyLayerResult layerResult = futureIdentifyLayer.get();
List<GeoElement> resultGeoElements = layerResult.getElements();
if (!resultGeoElements.isEmpty()) {
for (Object element : resultGeoElements) {
if (element instanceof Feature) {
Feature mFeatureGrafic = (Feature) element;
showCallout(mFeatureGrafic);
// 添加
mSelectedFeatures.add(mFeatureGrafic);
/* Map<String, Object> mQuerryString = mFeatureGrafic.getAttributes();
for(String key : mQuerryString.keySet()){
Log.i("Show"+key,String.valueOf(mQuerryString.get(key)));
}*/
}
}
}else {
mCallout.dismiss();
}
} catch (Exception e1) {
Toast.makeText(context, "选择失败!",Toast.LENGTH_SHORT).show();
}
}
});
return super.onSingleTapConfirmed(e);
}
// 控制旋转
@Override
public boolean onRotate(MotionEvent event,double rotationAngle) {
return false;
}
});
}
/**
* 查询并修改shp要素数据 --- 第二种方法
* */
public void updataShpTwo(){
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
final Point clickPoint = mapView
.screenToLocation(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())));
int tolerance = 10;
double mapTolerance = tolerance * mMapView.getUnitsPerDensityIndependentPixel();
// create objects required to do a selection with a query
Envelope envelope = new Envelope(clickPoint.getX() - mapTolerance, clickPoint.getY() - mapTolerance,
clickPoint.getX() + mapTolerance, clickPoint.getY() + mapTolerance, mapView.getSpatialReference());
QueryParameters query = new QueryParameters();
query.setGeometry(envelope);
final ListenableFuture<FeatureQueryResult> featureQueryResultFuture = shpPointFileFTable.getFeatureLayer().selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW);
featureQueryResultFuture.addDoneListener(new Runnable() {
@Override
public void run() {
FeatureQueryResult featureQueryResult = null;
try {
featureQueryResult = featureQueryResultFuture.get();
} catch (ExecutionException ex) {
ex.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// create an Iterator
Iterator<Feature> iterator = featureQueryResult.iterator();
Feature feature = null;
int counter = 0;
while (iterator.hasNext()) {
feature = iterator.next();
counter++;
Log.d(TAG, "Selection #: " + counter + " Table name: " + feature.getFeatureTable().getTableName());
}
// Callout
showCallout(feature );
// 添加
mSelectedFeatures.add(feature);
/* Map<String, Object> mQuerryString = mFeatureGrafic.getAttributes();
for(String key : mQuerryString.keySet()){
Log.i("Show"+key,String.valueOf(mQuerryString.get(key)));
}*/
}
});
return super.onSingleTapConfirmed(e);
}
// 控制旋转
@Override
public boolean onRotate(MotionEvent event,double rotationAngle) {
return false;
}
});
}
查询shp数据
/**
* 查询shp方式
*/
public void queryByIdentify() {
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context,mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
android.graphics.Point screenPoint = new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY()));
final ListenableFuture<IdentifyLayerResult> identifyLayerResultListenableFuture = mapView.identifyLayerAsync(
shapefileFeatureTable.getFeatureLayer(), screenPoint, 12, false, 10);
identifyLayerResultListenableFuture.addDoneListener(new Runnable() {
@Override
public void run() {
try {
IdentifyLayerResult identifyLayerResult = identifyLayerResultListenableFuture.get();
String name = identifyLayerResult.getLayerContent().getName();
List<GeoElement> elements = identifyLayerResult.getElements();
for (GeoElement element : elements) {
Map<String, Object> attributes = element.getAttributes();
Geometry geometry = element.getGeometry();
//高亮显示选中区域
mMapView.setViewpointGeometryAsync(geometry.getExtent());
SimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE,Color.argb(100, 255, 0, 0), 10);
SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 3);
SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.RED, lineSymbol);
if (mGraphicsOverlay != null) {
ListenableList<Graphic> graphics = mGraphicsOverlay.getGraphics();
if (graphics.size() > 0) {
graphics.removeAll(graphics);
}
}
Graphic graphic = new Graphic(geometry, simpleMarkerSymbol);
mGraphicsOverlay.getGraphics().add(graphic);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
});
return super.onSingleTapConfirmed(e);
}
});
}
/**
* 查询shp方式1:selectFeaturesAsync(查询不出数据)
*/
public void queryBySelectFeaturesAsync() {
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
shapefileFeatureTable.getFeatureLayer().clearSelection();
final Point clickPoint = mapView.screenToLocation(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())));
int tolerance = 10;
double mapTolerance = tolerance * mapView.getUnitsPerDensityIndependentPixel();
SpatialReference spatialReference = mapView.getSpatialReference();
Envelope envelope = new Envelope(clickPoint.getX() - mapTolerance, clickPoint.getY() - mapTolerance,
clickPoint.getX() + mapTolerance, clickPoint.getY() + mapTolerance, spatialReference);
QueryParameters query = new QueryParameters();
query.setGeometry(envelope);
query.setSpatialRelationship(QueryParameters.SpatialRelationship.WITHIN);
final ListenableFuture<FeatureQueryResult> future = featureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW);
future.addDoneListener(new Runnable() {
@Override
public void run() {
try {
FeatureQueryResult result = future.get();
//mFeatureLayer.getFeatureTable().deleteFeaturesAsync(result);
Iterator<Feature> iterator = result.iterator();
int counter = 0;
while (iterator.hasNext()) {
counter++;
Feature feature = iterator.next();
Map<String, Object> attributes = feature.getAttributes();
for (String key : attributes.keySet()) {
Log.e("xyh" + key, String.valueOf(attributes.get(key)));
}
//高亮显示选中区域
featureLayer.selectFeature(feature);
Geometry geometry = feature.getGeometry();
mMapView.setViewpointGeometryAsync(geometry.getExtent());
//也可以通过添加graphic高亮显示选中区域
//
// SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 3);
// SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.RED, lineSymbol);
//
// if (mGraphicsOverlay != null) {
// ListenableList<Graphic> graphics = mGraphicsOverlay.getGraphics();
// if (graphics.size() > 0) {
// graphics.removeAll(graphics);
// }
// }
// Graphic graphic = new Graphic(geometry, fillSymbol);
// mGraphicsOverlay.getGraphics().add(graphic);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return super.onSingleTapConfirmed(e);
}
});
}
/**
* 查询shp方式2:queryFeaturesAsync(查询不出数据)
*/
public void queryByQueryFeaturesAsync() {
mapView.setOnTouchListener(new DefaultMapViewOnTouchListener(context, mapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
android.graphics.Point screenPoint = new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY()));
Point clickPoint = mapView.screenToLocation(screenPoint);
QueryParameters query = new QueryParameters();
query.setGeometry(clickPoint);// 设置空间几何对象
FeatureTable mTable = featureLayer.getFeatureTable();//得到查询属性表
final ListenableFuture<FeatureQueryResult> featureQueryResult = mTable.queryFeaturesAsync(query);
featureQueryResult.addDoneListener(new Runnable() {
@Override
public void run() {
try {
FeatureQueryResult result = featureQueryResult.get();
Iterator<Feature> iterator = result.iterator();
int counter = 0;
while (iterator.hasNext()) {
counter++;
Feature feature = iterator.next();
Map<String, Object> attributes = feature.getAttributes();
for (String key : attributes.keySet()) {
Log.e("xyh" + key, String.valueOf(attributes.get(key)));
}
//高亮显示选中区域
Geometry geometry = feature.getGeometry();
mMapView.setViewpointGeometryAsync(geometry.getExtent());
//高亮点显示选中区域
SimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.RED, 3);
SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.RED, 3);
SimpleFillSymbol fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.RED, lineSymbol);
if (mGraphicsOverlay != null) {
ListenableList<Graphic> graphics = mGraphicsOverlay.getGraphics();
if (graphics.size() > 0) {
graphics.removeAll(graphics);
}
}
Graphic graphic = new Graphic(geometry, fillSymbol);
mGraphicsOverlay.getGraphics().add(graphic);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return super.onSingleTapConfirmed(e);
}
});
}