前言
在前文已经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 david_ocean@163.com
- * @date 2013年10月30日 下午2:46:06
- *
- */
- public class DrawGraphicTouchListener extends MapOnTouchListener{
- // List<Point> pointsList=new ArrayList<Point>();
- 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 david_ocean@163.com
- * @date 2013年10月30日 下午2:46:06
- *
- */
- public class DrawGraphicTouchListener extends MapOnTouchListener{
- // List<Point> pointsList=new ArrayList<Point>();
- 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来下载该程序和离线切片地图。