ArcGIS for Android 体验(二) 绘制标绘

前言

在前文已经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的构造器的说明:

工程的建立

Menu的设定

在谈及完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;
			
		}
		
	}

}


可通过 http://download.csdn.net/detail/david_ocean/6477961来下载该程序和离线切片地图。

-----2013.10.30

-----David.Ocean






你可能感兴趣的:(ArcGIS,for,Android)