在前文已经http://blog.csdn.net/david_ocean/article/details/13296125中已经说到了对于地图图层的添加,其中特意说明了矢量图层的特质,在线矢量图层的加载和离线矢量图层的权限问题。
而本章的重点,是通过矢量图层进行操作,完成绘制。在ArcGIS for Android中,矢量图层的离线加载是需要认证许可。而在mapview中自行创建的矢量图层,可以将矢量信息进行表达,只是无法存储而已。
查看API中,可知道,对于加载的在线矢量地图ArcGISFeatureLayer是GraphicsLayer的子类,而我们绘制信息的图层也是GraphicLayer图层。
在ArcGIS for Android中,对于绘制的基本元素之一是Geometry,例如我们最常用也是最重要的就是:point,polyline,polygon。他们包含地理坐标信息和类型信息。在ArcGIS for Android中对于他们有自己的定义,请看看示意:
最最基本的单位是point。然后通过point可以自己组成一个点要素,然后若是想要组成polyline或者polyline的话,那么需要进行更多操作。Polyline和polygon的基本组成要素是line,可以理解成线段。而line是需要point来组成。每个line有且仅有两个point,也就是两点组成一段,然后又line来组成polyline和polygon。这和我们以往的理解有些不同。在以往的理解中,polyline和polygon都是由point组成的。所以现在有了这个不同,在这里本人就要特别提一下。
大家看到这个point,polyline以及polygon了,这些只能算是要素,自己是没有外形的。我们需要为其设定外形,点的形状,颜色,大小。线的粗细,颜色,虚线与否。面的透明度,颜色以及填充物样式等。这些在ArcGIS for Android中为Symbol中进行设定。Symbol的基本结构大概如此:
一个真正的标绘Graphic的基本组成单元是由Geometry和Symbol共同完成的,所以我们在设定标绘的时候,要使用两者一同设定。下文是Graphic的构造器的说明:
在谈及完Graphic的结构原理后,现在需要做的是对于工程的建立来实现标绘的添加。
在界面中,不修改main.xml,整个屏幕不添加控件,让其显示最完成。然后所有操作设定在menu上面。我分成点,线,面三种类型的绘制,每种类型有一些选择来区别标绘内容。
Menu设定的实现代码:
//菜单的加载
@Override
public boolean onCreateOptionsMenu(Menu menu){
//加载点选择菜单
SubMenu pointSubMenu=menu.addSubMenu("点");
pointSubMenu.setHeaderTitle("选择绘制的点");
pointSubMenu.add(0, 0, 0, "图点");
pointSubMenu.add(0, 1, 0, "红点");
pointSubMenu.add(0, 2, 0, "蓝点");
pointSubMenu.add(0, 3, 0, "绿点");
//加载线选择菜单
SubMenu lineSubMenu=menu.addSubMenu("线");
lineSubMenu.setHeaderTitle("选择绘制的线");
lineSubMenu.add(1,0,0,"白线");
lineSubMenu.add(1, 1, 0, "红线");
lineSubMenu.add(1, 2, 0, "蓝虚线");
lineSubMenu.add(1, 3, 0, "黄粗线");
//加载面选择菜单
SubMenu gonMenu=menu.addSubMenu("面");
gonMenu.setHeaderTitle("选择绘制的面");
gonMenu.add(2, 0, 0, "红面");
gonMenu.add(2, 1, 0, "绿面半透明");
gonMenu.add(2,2,0,"蓝面虚线填充");
return super.onCreateOptionsMenu(menu);
}
//菜单项被单击的事件
@Override
public boolean onOptionsItemSelected(MenuItem mi){
isChoose=true;
//判断是在哪个groupid里面的
switch (mi.getGroupId()) {
case 0:
drawType=Geometry.Type.POINT;
switch (mi.getItemId()) {
case 0:
Drawable drawable=this.getResources().getDrawable(R.drawable.icon);
symbol=new PictureMarkerSymbol(drawable);
break;
case 1:
symbol=new SimpleMarkerSymbol(Color.RED, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
case 2:
symbol=new SimpleMarkerSymbol(Color.BLUE, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
case 3:
symbol=new SimpleMarkerSymbol(Color.GREEN, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
default:
break;
}
break;
case 1:
drawType=Geometry.Type.POLYLINE;
switch (mi.getItemId()) {
case 0:
symbol=new SimpleLineSymbol(Color.WHITE, 8, SimpleLineSymbol.STYLE.SOLID);
break;
case 1:
symbol=new SimpleLineSymbol(Color.RED, 8, SimpleLineSymbol.STYLE.SOLID);
break;
case 2:
symbol=new SimpleLineSymbol(Color.BLUE, 10, SimpleLineSymbol.STYLE.DASH);
break;
case 3:
symbol=new SimpleLineSymbol(Color.YELLOW, 18, SimpleLineSymbol.STYLE.SOLID);
break;
default:
break;
}
break;
case 2:
drawType=Geometry.Type.POLYGON;
switch (mi.getItemId()) {
case 0:
fillSymbol=new SimpleFillSymbol(Color.RED, SimpleFillSymbol.STYLE.SOLID);
fillSymbol.setAlpha(100);
break;
case 1:
fillSymbol=new SimpleFillSymbol(Color.GREEN);
fillSymbol.setAlpha(50);
break;
case 2:
fillSymbol=new SimpleFillSymbol(Color.BLUE,SimpleFillSymbol.STYLE.BACKWARD_DIAGONAL);
fillSymbol.setAlpha(100);
break;
case 3:
break;
default:
break;
}
break;
default:
break;
}
return false;
}
界面效果为:
点击点的时候的效果:
点击线和点击面的效果差不多。
在Mapview中设定触碰监听,这样能够通过监听器来让系统完成绘制作用。我在这里这写一个类来继承MapOnTouchListener,重写单击事件来确定各个绘制。代码如下:
/**
*
* @ClassName: DrawGraphicTouchListener
* @Description: 对触碰点击事件进行重写,使得能够进行绘制
* @author David.Ocean [email protected]
* @date 2013年10月30日 下午2:46:06
*
*/
public class DrawGraphicTouchListener extends MapOnTouchListener{
// List pointsList=new ArrayList();
Point ptStart=null;
Point ptPrevious=null;
Polygon polygon=null;
public DrawGraphicTouchListener(Context context, MapView view) {
super(context, view);
}
@Override
public boolean onSingleTap (MotionEvent point){
if(isChoose==true){
ptPrevious=null;
ptStart=null;
polygon=null;
}
float x=point.getX();
float y=point.getY();
Point ptCurrent=map.toMapPoint(x,y);
if (drawType==Geometry.Type.POINT) {
Graphic pGraphic=new Graphic(ptCurrent, symbol);
graphicsLayer.addGraphic(pGraphic);
}
else {
// pointsList.add(ptCurrent);
if (ptStart==null) {
ptStart=ptCurrent;
Graphic pgraphic=new Graphic(ptStart,new SimpleMarkerSymbol(Color.RED, 8, SimpleMarkerSymbol.STYLE.CIRCLE));
graphicsLayer.addGraphic(pgraphic);
}
else {
Graphic pGraphic=new Graphic(ptCurrent,new SimpleMarkerSymbol(Color.RED, 8, SimpleMarkerSymbol.STYLE.CIRCLE));
graphicsLayer.addGraphic(pGraphic);
Line line=new Line();
line.setStart(ptPrevious);
line.setEnd(ptCurrent);
if(drawType==Geometry.Type.POLYLINE){
Polyline polyline=new Polyline();
polyline.addSegment(line, true);
Graphic iGraphic=new Graphic(polyline, symbol);
graphicsLayer.addGraphic(iGraphic);
}
else if (drawType==Geometry.Type.POLYGON) {
if(polygon==null){
polygon=new Polygon();
}
polygon.addSegment(line, false);
Graphic gGraphic=new Graphic(polygon, fillSymbol);
graphicsLayer.addGraphic(gGraphic);
}
}
}
ptPrevious=ptCurrent;
isChoose=false;
return false;
}
}
在程序中的表现效果为:
代码说明:
在代码中,对于单击事件中,每按下一个点,就是屏幕中获取到的MotionEvent,这不是地图坐标系下的点,所以要经过转化,调用方法map.toMapPoint(x,y),这样就可以获得在地图上的点。
然后根据drawType来确定geometry的类型,从而确定应该执行的方法。对于点的操作,直接将geometry和symbol导入,形成graphic,然后添加到graphiclayer即可。
if (drawType==Geometry.Type.POINT) {
Graphic pGraphic=new Graphic(ptCurrent, symbol);
graphicsLayer.addGraphic(pGraphic);
}
如果是polyline和polygon的话,则需要设定line,并把其加入到polyline或者polygon中。
Line line=new Line();
line.setStart(ptPrevious);
line.setEnd(ptCurrent);
判断之后,根据线或者面来分别处理。
if(drawType==Geometry.Type.POLYLINE){
Polyline polyline=new Polyline();
polyline.addSegment(line, true);
Graphic iGraphic=new Graphic(polyline, symbol);
graphicsLayer.addGraphic(iGraphic);
}
else if (drawType==Geometry.Type.POLYGON) {
if(polygon==null){
polygon=new Polygon();
}
polygon.addSegment(line, false);
Graphic gGraphic=new Graphic(polygon, fillSymbol);
graphicsLayer.addGraphic(gGraphic);
}
其中,需要注意的是polygon不能在判断语句中初始化,因为他是由至少三个点才能组成一个面,所以需要在触碰监听类刚建立的时候进行初始化创建。
在逻辑层判断的时候,当再次进行菜单选择的时候,则存储的点和polygon需要进行清空,那么就要一层逻辑判断。在菜单单击事件出,给boolean型的ischoose赋值,来进行判断。
public boolean onOptionsItemSelected(MenuItem mi){
isChoose=true;
那么在触碰监听的时候,则直接判断时候根据判断来确定是否清零。
public boolean onSingleTap (MotionEvent point){
if(isChoose==true){
ptPrevious=null;
ptStart=null;
polygon=null;
}
这样可以保证不会出现所有点都是连续的情况。
关于graphicLayer的设计和设定,首先要将Graphic进行设定。而70%工作就是将geometry和symbol的各个类型分配清楚,此为关键。然后在随后的触碰监听中,自己设定逻辑判断来使得各个graphic能够生成正确,并最终能够添加到graphicLayer中。
在逻辑判断中,要记住,polyline和polygon的基本单位是line。调用addSegement()方法来添加。Polyline和polygon很多相似,但是要记住,一条线可以组成polyline但是不能组成polygon,所以要区别对待。
菜单的使用上面,要设定清楚,往后的例子中,对菜单的使用会更多。
最后,将整个类的完整代码贴出来:
/* Copyright 2012 ESRI
*
* All rights reserved under the copyright laws of the United States
* and applicable international laws, treaties, and conventions.
*
* You may freely redistribute and use this sample code, with or
* without modification, provided you include the original copyright
* notice and use restrictions.
*
* See the Sample code usage restrictions document for further information.
*
*/
package com.esri.arcgis.android.samples.addlayer;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SubMenu;
import com.esri.android.map.GraphicsLayer;
import com.esri.android.map.MapOnTouchListener;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISLocalTiledLayer;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.Line;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polygon;
import com.esri.core.geometry.Polyline;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.PictureMarkerSymbol;
import com.esri.core.symbol.SimpleFillSymbol;
import com.esri.core.symbol.SimpleLineSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.Symbol;
/**
* Adds a layer statically and dynamically and toggles the visibility of top layer
* with a single tap
*
*/
public class AddLayer extends Activity {
private MapView map = null;
//Dynamic layer URL from ArcGIS online
/* String dynamicMapURL =
"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer";
ArcGISTiledMapServiceLayer basemap;
ArcGISDynamicMapServiceLayer dynamicLayer;*/
private GraphicsLayer graphicsLayer;
// 设定绘制的类型
private Geometry.Type drawType=null;
private Symbol symbol=null;
private SimpleFillSymbol fillSymbol=null;
//判断是否发生菜单选择事件
private boolean isChoose;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Retrieve the map and initial extent from XML layout
map = (MapView)findViewById(R.id.map);
/*map.addLayer(new ArcGISDynamicMapServiceLayer(
"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"));*/
/*basemap = new ArcGISTiledMapServiceLayer(this.getResources().getString(
R.string.basemap_url));
map.addLayer(basemap);
*/
/*dynamicLayer = new ArcGISDynamicMapServiceLayer(this.getResources()
.getString(R.string.dynamiclayer_url));
map.addLayer(dynamicLayer);*/
//Creates a dynamic layer using service URL
/* ArcGISDynamicMapServiceLayer dynamicLayer = new ArcGISDynamicMapServiceLayer(dynamicMapURL);
//Adds layer into the 'MapView'
map.addLayer(dynamicLayer);*/
//加载在线切片地图
/*ArcGISTiledMapServiceLayer tiledMapServiceLayer=new ArcGISTiledMapServiceLayer(
"http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
map.addLayer(tiledMapServiceLayer);*/
//加载离线切片地图
ArcGISLocalTiledLayer localtitleLayer=new ArcGISLocalTiledLayer("file:///mnt/sdcard/basemap/东直门.tpk");
map.addLayer(localtitleLayer);
//加载在线矢量地图
/*ArcGISFeatureLayer featureLayer=new ArcGISFeatureLayer("http://192.168.112.112:6080/arcgis/rest"
+ "/services/Feature/edit_polygon/MapServer/0", MODE.SNAPSHOT);
map.addLayer(featureLayer);*/
//设定用户进入许可
/*UserCredentials creds = new UserCredentials();
creds.setUserAccount("rick", "rick@esri");
//加载在线需验证的矢量地图
ArcGISFeatureLayer secureFeatureLayer = new ArcGISFeatureLayer("https://servicesbeta.esri.com/ArcGIS/rest"
+ "/services/SanJuan/ColoradoCounties/MapServer/0",
MODE.SNAPSHOT,creds);
map.addLayer(secureFeatureLayer);*/
//setContentView(map);
map.enableWrapAround(true);
map.setEsriLogoVisible(true);
//加载graphiclayer
graphicsLayer=new GraphicsLayer();
map.addLayer(graphicsLayer);
//设定绘制的监听事件,让其能够绘制
DrawGraphicTouchListener drawgraphictouchlistener=new DrawGraphicTouchListener(this,map);
map.setOnTouchListener(drawgraphictouchlistener);
}
//菜单的加载
@Override
public boolean onCreateOptionsMenu(Menu menu){
//加载点选择菜单
SubMenu pointSubMenu=menu.addSubMenu("点");
pointSubMenu.setHeaderTitle("选择绘制的点");
pointSubMenu.add(0, 0, 0, "图点");
pointSubMenu.add(0, 1, 0, "红点");
pointSubMenu.add(0, 2, 0, "蓝点");
pointSubMenu.add(0, 3, 0, "绿点");
//加载线选择菜单
SubMenu lineSubMenu=menu.addSubMenu("线");
lineSubMenu.setHeaderTitle("选择绘制的线");
lineSubMenu.add(1,0,0,"白线");
lineSubMenu.add(1, 1, 0, "红线");
lineSubMenu.add(1, 2, 0, "蓝虚线");
lineSubMenu.add(1, 3, 0, "黄粗线");
//加载面选择菜单
SubMenu gonMenu=menu.addSubMenu("面");
gonMenu.setHeaderTitle("选择绘制的面");
gonMenu.add(2, 0, 0, "红面");
gonMenu.add(2, 1, 0, "绿面半透明");
gonMenu.add(2,2,0,"蓝面虚线填充");
return super.onCreateOptionsMenu(menu);
}
//对菜单的点击事件
//菜单项被单击的事件
@Override
public boolean onOptionsItemSelected(MenuItem mi){
isChoose=true;
//判断是在哪个groupid里面的
switch (mi.getGroupId()) {
case 0:
drawType=Geometry.Type.POINT;
switch (mi.getItemId()) {
case 0:
Drawable drawable=this.getResources().getDrawable(R.drawable.icon);
symbol=new PictureMarkerSymbol(drawable);
break;
case 1:
symbol=new SimpleMarkerSymbol(Color.RED, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
case 2:
symbol=new SimpleMarkerSymbol(Color.BLUE, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
case 3:
symbol=new SimpleMarkerSymbol(Color.GREEN, 14, SimpleMarkerSymbol.STYLE.CIRCLE);
break;
default:
break;
}
break;
case 1:
drawType=Geometry.Type.POLYLINE;
switch (mi.getItemId()) {
case 0:
symbol=new SimpleLineSymbol(Color.WHITE, 8, SimpleLineSymbol.STYLE.SOLID);
break;
case 1:
symbol=new SimpleLineSymbol(Color.RED, 8, SimpleLineSymbol.STYLE.SOLID);
break;
case 2:
symbol=new SimpleLineSymbol(Color.BLUE, 10, SimpleLineSymbol.STYLE.DASH);
break;
case 3:
symbol=new SimpleLineSymbol(Color.YELLOW, 18, SimpleLineSymbol.STYLE.SOLID);
break;
default:
break;
}
break;
case 2:
drawType=Geometry.Type.POLYGON;
switch (mi.getItemId()) {
case 0:
fillSymbol=new SimpleFillSymbol(Color.RED, SimpleFillSymbol.STYLE.SOLID);
fillSymbol.setAlpha(100);
break;
case 1:
fillSymbol=new SimpleFillSymbol(Color.GREEN);
fillSymbol.setAlpha(50);
break;
case 2:
fillSymbol=new SimpleFillSymbol(Color.BLUE,SimpleFillSymbol.STYLE.BACKWARD_DIAGONAL);
fillSymbol.setAlpha(100);
break;
case 3:
break;
default:
break;
}
break;
default:
break;
}
return false;
}
@Override
protected void onPause() {
super.onPause();
map.pause();
}
@Override
protected void onResume() {
super.onResume();
map.unpause();
}
/**
*
* @ClassName: DrawGraphicTouchListener
* @Description: 对触碰点击事件进行重写,使得能够进行绘制
* @author David.Ocean [email protected]
* @date 2013年10月30日 下午2:46:06
*
*/
public class DrawGraphicTouchListener extends MapOnTouchListener{
// List pointsList=new ArrayList();
Point ptStart=null;
Point ptPrevious=null;
Polygon polygon=null;
public DrawGraphicTouchListener(Context context, MapView view) {
super(context, view);
}
@Override
public boolean onSingleTap (MotionEvent point){
if(isChoose==true){
ptPrevious=null;
ptStart=null;
polygon=null;
}
float x=point.getX();
float y=point.getY();
Point ptCurrent=map.toMapPoint(x,y);
if (drawType==Geometry.Type.POINT) {
Graphic pGraphic=new Graphic(ptCurrent, symbol);
graphicsLayer.addGraphic(pGraphic);
}
else {
if (ptStart==null) {
ptStart=ptCurrent;
Graphic pgraphic=new Graphic(ptStart,new SimpleMarkerSymbol(Color.RED, 8, SimpleMarkerSymbol.STYLE.CIRCLE));
graphicsLayer.addGraphic(pgraphic);
}
else {
Graphic pGraphic=new Graphic(ptCurrent,new SimpleMarkerSymbol(Color.RED, 8, SimpleMarkerSymbol.STYLE.CIRCLE));
graphicsLayer.addGraphic(pGraphic);
Line line=new Line();
line.setStart(ptPrevious);
line.setEnd(ptCurrent);
if(drawType==Geometry.Type.POLYLINE){
Polyline polyline=new Polyline();
polyline.addSegment(line, true);
Graphic iGraphic=new Graphic(polyline, symbol);
graphicsLayer.addGraphic(iGraphic);
}
else if (drawType==Geometry.Type.POLYGON) {
if(polygon==null){
polygon=new Polygon();
}
polygon.addSegment(line, false);
Graphic gGraphic=new Graphic(polygon, fillSymbol);
graphicsLayer.addGraphic(gGraphic);
}
}
}
ptPrevious=ptCurrent;
isChoose=false;
return false;
}
}
}
-----2013.10.30
-----David.Ocean