关于基于Android上ArcGIS Server GP服务的调用,已经有前辈给出了很好的例子:html
http://blog.csdn.net/esrichinacd/article/details/9231815java
以及官方的帮助文档:android
https://developers.arcgis.com/android/sample-code/viewshed/web
详细经过仔细学习上面的内容,您也能够基本了解GP服务的使用过程。算法
本文咱们主要将如下三部份内容:api
1.学会使用使用ArcMap构建等值线GP服务模型服务器
2.学会使用ArcGIS Server发布咱们建立的GP服务模型异步
3.学会ArcGIS Runtime for Android下异步调用GP服务,绘制等值线ide
其中前两部分可本博客其余文章有详细说明可参考:函数
http://www.cnblogs.com/potential/archive/2012/10/27/2742355.html
这篇文章使用的是10.0的ArcGIS Server,而这里咱们使用的是10.1的 ArcGIS Server,所以在此咱们也会提到10.1下如何来发布咱们的服务。若是您熟悉GP服务的发布过程,可直接跳转至第三部分。
第三部分的内容主要解决如下几个问题:
1.若是根据现有的坐标信息和对应的属性数据,构造相应的地理要素?例如:如今有某个省的个监测点的降雨数据(监测点经纬度,及监测点的降雨量),那么如何在地图上对这些监测点的数据进行反映?
2.如何在Android平台上根据这些监测点的数据来对整个地区进行插值呢?并将等值线添加到Android移动平台的地图中?
3.如何根据等值线的值,动态设置其不一样的颜色呢?
1、使用ArcMap构建等值线的GP服务模型
既然咱们要建立等值线那么,确定须要绘制等值线的数据,即进行等值线绘制的点。而等值线又是怎么获得的呢?很简单,根据这些点的位置和其表示的值(如该店的降雨量,浓度值等)对其进行插值。插值的过程就是根据已有的点来计算某一未知区域的点的浓度值。
经常使用的插值方法有拉格朗日插值,克里金插值,反距离插值,样条函数插值等。通常状况下咱们选择反距离和克里金插值,若是想获得很是平滑的插值结果,则能够采用三次样条插值。
插值完成以后,实际上获得的是更多的点,而绘制等值线就是将具备相同属性值的点链接起来获得等值线。一般状况下咱们须要本身代码完成插值过程和追踪等值点的算法。并链接等值点。可是在ArcGIS中有内置的插值工具和等值线工具供咱们使用。可是在ArcGIS中各个功能模块都是一个单独的工具,而经过上面的分析咱们知道绘制等值线至少有两个过程:1.插值,2.依据插值结果绘制等值线
所以依据上面的分析咱们经过ArcMap的Model Builder能够构建以下的工具模型:
可是一般状况下上述获得的等值线有可能不会很平滑,若是须要较平滑的等值线能够再加上一个平滑工具,如:
经过加上平滑工具,设置平滑容差,能够获得较为平滑的等值线。
2、 发布GArcMap构建的GP服务模型
在ArcGIS Server 10.1中,发布GP服务的方式较以前有所改变,再也不是发布GP服务模型自己,而是发布在GP服务模型在ArcMap运行以后的结果,如图所示:
而后出现发布服务的选项:
Overwrite an existing service:覆盖当前的ArcGIS Server 服务。
保存为sd文件以后,登录ArcGIS Server Manager页面,点击发布服务:
而后点击选择文件,选择以前在ArcMap中保存的服务定义文件(.sd).而后点击下一步。
以后选择发布服务的名称,和所在服务的目录。
最后点击下一步,发布服务。在服务发布以后登录服务的rest页面能够查看服务的具体参数信息:
接下来咱们就须要开始编码来调用咱们这里的GP服务实现绘制等值线的功能。到此咱们的绘制等值线的GP服务以及发布完成,接下来就是编码的过程。固然为了确保您的GP服务可以正确运行,建议在编码以前,经过ArcMap来验证一下您的GP服务。验证过程可参考以前的博文,鉴于篇幅在此再也不赘述。
3、 ArcGIS Runtime for Android 调用异步GP服务绘制等值线
本文开发环境:
调试过程在真机上实现
3.1 ArcGIS Runtime for Android GP服务调用过程
首先咱们须要了解GP服务的几个问题:
3.1.1.查看当前GP服务是异步仍是同步
ArcGIS 的GP服务有异步和同步的两种模式,默认状况下(ArcGIS Server 10.1)使用的是异步模式。
经过REST页面的参数咱们也能够知道GP服务时何种模式:
或者
Asynchronous表示的是异步模式,Submit Job也表示异步模式,他们是对应的。
3.11.2 异步和同步的区别
异步模式一般适用于模型较复杂,运行时间较长的GP服务,而同步适用于模型较简单,运行时间较短的GP服务。
3.2 ArcGIS Runtime 调用GP服务的详细过程
ArcGIS Runtime for Android 中调用GP服务的核心类是:Geoprocessor.该类封装了调用GP服务所需的方法。其中较为重要的以下表所示:
方法名称 | 返回值 | 描述 |
execute(List<GPParameter> parameters) | GPResultResource | 用于开始调用同步GP服务,GP服务执行完成以后,返回GP服务的结果 |
submitJob(List<GPParameter> parameters) | GPJobResource | 用于开始调用异步GP服务,并返回本次GP服务调用的相关信息(如标示id,状态等)。和同步不一样的是每一次异步调用GP服务都会生成一个jobID来标示每一次GP调用的任务,经过这个jobId咱们能够找到该次GP服务调用的状态,结果。 |
checkJobStatus(String jobId) | GPJobResource | 检查指定jobid对于GP服务的当前状态,如服务是否提交,是否执行成功了,或者是否执行失败了等。 |
getResultData(String jobId, String parameterName) | GPParameter | 根据指定的jobid,获取指定的名称的GP服务结果,注意全部的GP服务参数类型都继承至GPParameter |
getResultImage(String jobId, String parameterName) | GPParameter | 获取GP服务的结果,并以图片的方式返回(GPMapImage,GPRasterDataLayer),主要用于结果地图服务 |
Geoprocessor构造函数有两个重载,都必须传入GP服务的地址。
下面咱们以上述绘制等值线的基础为例子,来具体说明如何让调用GP服务。
经过以前绘制等值线GP服务的REST页面,咱们知道请求GP服务须要三个输入参数和一个输出参数(计算结果这里即表示等值线),咱们先看一看输入参数:
而在调用GP服务时,咱们须要将上述的参数添加到GPParameter类的集合中(List<GPParameter>),最后调用GP服务传递的是List< GPParameter>集合。
下面咱们来构造上述GP服务参数,首先咱们新建一个类,取名:GPService,而后定义所所需的参数:
private static final String FORECAST = "FORECAST";// 调试标示 private Geoprocessor geoprocessor = null;// GP服务变量 private GPFeatureRecordSetLayer forecastPointFeatures = null;// 预测点要素集 private GPDouble contour_interval = null;// 等值线间距 private GPLinearUnit smoothing_Tolerance = null;// 平滑容差 private ArrayList<GPParameter> gpParameters = null;// GP服务参数集合 public static GraphicsLayer contourLineGraphicsLayer;// 等值线图层 public GraphicsLayer gridPointGraphicsLayer;// 网格点图层 private static MapView mapView = null;// MapView控件 private ArrayList<Graphic> forecastGraphicList = null;// 预测点集合 double startx, starty;// 绘制网格的起始 private Timer checkStatusTimer;// 时钟,用于轮询GP服务执行的状态
并实例化:
//实例化参数 geoprocessor = new Geoprocessor("http://192.168.1.181:6080/arcgis/rest/services/Interpolation/ContourServiceWithSmoothOption/GPServer/ContourToolWithSmoothOption"); forecastPointFeatures = new GPFeatureRecordSetLayer("ForecastPoints"); contour_interval = new GPDouble("Contour_interval"); smoothing_Tolerance = new GPLinearUnit("Smoothing_Tolerance"); gpParameters = new ArrayList<GPParameter>(); gridPointGraphicsLayer = new GraphicsLayer(GraphicsLayer.RenderingMode.DYNAMIC); contourLineGraphicsLayer = new GraphicsLayer(GraphicsLayer.RenderingMode.DYNAMIC); forecastGraphicList = new ArrayList<Graphic>(); startx = x; starty = y; //取得当前的MapView对象 setMapView(map); //设置GP服务的默认参数 contour_interval.setValue(3);//设置默认等值线间距是3 smoothing_Tolerance.setDistance(30);//设置默认容差是30 smoothing_Tolerance.setUnits("esriMeters");//设置容差单位为米
说明:10.2的ArcGIS Runtime for Android 的API中对于GraphicsLayer有了很大的改进,在声明GraPhicsLayer对象时能够指定Gphics的渲染模式,当指定为DYNAMIC时能够极大优化Graphic的加载速度,从而提升用户体验。同时为了简化整个过程,这里咱们用于绘制等值线的点,以及其对于的浓度值并非真实的数据,咱们将采用代码来生成模拟的数据,具体过程是:用户输入一个点,以该点为中心生成21x41个网格点。固然换成真实数据的过程同样。下面是生成模拟数据的方法,这里咱们使用了一个椭圆的函数来构造浓度值,所以咱们预期的等值线应该是一个一个的椭圆,此外还须要注意的是用于预测的点要素集必须包含concentration属性,由于咱们这里服务器端的GP服务是根据concentration属性来绘制等值线的。要素的的属性数据是以键值对的方式存放的。
private void CreateGrid() { int drawOrder = 1; for (int i = -10; i <= 10; i++) { for (int j = -20; j <= 20; j++) { Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put("concentration", Math.pow(i, 2) / 10 + Math.pow(j, 2) / 20); attributes.put("xindex", i); attributes.put("yindex", j); drawOrder++; Graphic graphic = createGraphic(startx + i * 400, starty + j * 200, attributes, drawOrder); //网格点图层 gridPointGraphicsLayer.addGraphic(graphic); //预测点要素集 forecastGraphicList.add(graphic); } } }
在构造完所需的参数以后,咱们添加集合辅助方法:
//设置等值线间距参数 public void setContourIntervalValue(double interval) { contour_interval.setValue(interval); } //设置平滑容差 public void setSmoothToleranceValue(double tolerance, String units) { smoothing_Tolerance.setDistance(tolerance); smoothing_Tolerance.setUnits(units); } //建立渐变的颜色 public int[] createColorSet() { int[] colors = new int[6]; for (int i = 0; i <= 5; i++) { colors[i] = Color.argb(254, i * 51, 255 - i * 51, 0); } return colors; } //构建用于分类渲染的样式 public ClassBreaksRenderer createClassBreaksRenderer(int[] colors, double maxValue, double minValue) { ClassBreaksRenderer contourBreaksRenderer = new ClassBreaksRenderer(); double stepValue = (Math.ceil(maxValue) - Math.floor(minValue)) / colors.length; for (int i = 0; i < colors.length; i++) { ClassBreak classBreak = new ClassBreak(); double max = (i + 1) * stepValue + Math.floor(minValue); double min = i * stepValue + Math.floor(minValue); classBreak.setClassMaxValue(max); classBreak.setClassMinValue(min); classBreak.setSymbol(new SimpleLineSymbol(colors[i], 3, com.esri.core.symbol.SimpleLineSymbol.STYLE.SOLID)); contourBreaksRenderer.addClassBreak(classBreak); } return contourBreaksRenderer; } //够着GraPhic对象,并输入指定的属性数据 public Graphic createGraphic(double x, double y, Map<String, Object> attributes, int drawOrder) { Graphic graphic = new Graphic(new Point(x, y), new SimpleMarkerSymbol(Color.BLUE, 6, STYLE.CIRCLE), attributes, drawOrder); return graphic; }
注意:在ArcGIS Runtime for Android的API中,没有单独为Graphic提供添加属性(Attributes,若是您熟悉.net下的ArcGIS api,那么您应该知道能够经过Graphic的addAttributes方法来为Graphic添加属性),因为没有相似.net下的addAttributes方法,所以在须要给Graphic添加方法时只能经过两种方式:
定义好参数以及相关的辅助方法后,下面就来完成请求GP服务的代码,定义一个StartGPService方法:
为了使得请求GP服务时不影响前台的UI线程,所以咱们从新new一个线程,由于若是直接在UI线程来添加用于预测的网格点(21x41)会有卡顿的现象。
public void StartGPService() { new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub CreateGrid(); // 输入要素的要素类型,必须与GP服务所对应 forecastPointFeatures.setGeometryType(Type.POINT); // 设置坐标系为102100坐标系 forecastPointFeatures.setSpatialReference(SpatialReference.create(SpatialReference.WKID_WGS84_WEB_MERCATOR_AUXILIARY_SPHERE)); geoprocessor.setOutSR(forecastPointFeatures.getSpatialReference()); geoprocessor.setProcessSR(forecastPointFeatures.getSpatialReference()); // 将裁剪要素的坐标系设置为与预测点要素的坐标系一致 forecastPointFeatures.setGraphics(forecastGraphicList); for (Graphic testGraphic : forecastPointFeatures.getGraphics()) { Log.i(FORECAST, "(" + testGraphic.getAttributeValue("xindex").toString() + "," + testGraphic.getAttributeValue("yindex").toString() + ")=" + testGraphic.getAttributeValue("concentration").toString()); } gpParameters.add(forecastPointFeatures); gpParameters.add(contour_interval); Log.i(FORECAST, "Submitting job thread is: " + Thread.currentThread().getName()); getMapView().post(new Runnable() { public void run() { // TODO Auto-generated method stub getMapView().addLayer(gridPointGraphicsLayer); Log.i(FORECAST, "ADD FORECAST POINT TO LAYER COMPLETED"); } }); submitJobandPolling(geoprocessor, gpParameters); } }).start(); }
这里的submitJobandPolling方法以下,大体过程是:
1.提交当前的GP服务请求绘制等值线。
2.提交完成以后,取得当前的请求的JobId。
3.经过Timer,同时根据当前的JobId轮询该GP服务的执行状态
4.若是GP服务执行完成,则中止轮询
5.若是GP服务执行成功,则获取GP服务的结果:getResultData(jobId,"ContourLine"),获得GPParameter
6.由于是等值线,因此结果是线要素,所以咱们将GPParameter转为GPFeatureRecordSetLayer。
7.读取GPFeatureRecordSetLayer中的Graphic,即等值线
8.根据等值线的值(contour属性)分类渲染等值线。
9.将等值线图层添加到地图中
代码以下:
void submitJobandPolling(final Geoprocessor gp, List<GPParameter> params) { try { GPJobResource gpjobResource = gp.submitJob(params); JobStatus jobstatus = gpjobResource.getJobStatus(); final String jobid = gpjobResource.getJobID(); Log.i(FORECAST, "jobid " + jobid); Log.i(FORECAST, "jobstatus " + jobstatus); if (jobstatus != JobStatus.SUCCEEDED) { Log.i(FORECAST, "Start Check GP Status..."); checkStatusTimer = new Timer(); checkStatusTimer.schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub try { Log.i(FORECAST, "Checking Status is running..."); Log.i(FORECAST, "Polling thread is: " + Thread.currentThread().getName()); GPJobResource gpjobResource = gp.checkJobStatus(jobid); JobStatus status = gpjobResource.getJobStatus(); Log.i(FORECAST, "jobstatus " + status); boolean jobcomplete = false; if (status == JobStatus.CANCELLED || status == JobStatus.DELETED || status == JobStatus.FAILED || status == JobStatus.SUCCEEDED || status == JobStatus.TIMED_OUT) { jobcomplete = true; } if (jobcomplete) { if (status == JobStatus.SUCCEEDED) { Log.i(FORECAST, "START GETTING GP Service Result... "); GPParameter result = gp.getResultData(jobid, "ContourLine"); Log.i(FORECAST, "GETTING GP Service Result COMPLETED"); Log.i(FORECAST, "convert GP Service Result to GPFeatureRecordSetLayer"); GPFeatureRecordSetLayer resultLayer = (GPFeatureRecordSetLayer) result; Log.i(FORECAST, "convert GP Service Result Type completed"); Log.i(FORECAST, "Create Graphics from GP Service Result"); double maxContour = 0; double minContour = 0; for (Graphic graphic : resultLayer.getGraphics()) { if (Double.parseDouble(graphic.getAttributeValue("Contour").toString()) > maxContour) { maxContour = Double.parseDouble(graphic.getAttributeValue("Contour").toString()); } else if (Double.parseDouble(graphic.getAttributeValue("Contour").toString()) < minContour) { minContour = Double.parseDouble(graphic.getAttributeValue("Contour").toString()); } contourLineGraphicsLayer.addGraphic(graphic); } Log.i(FORECAST, "Create Render Colors"); int[] colors = createColorSet();// for (int i = 0; i < colors.length; i++) { Log.i(FORECAST, "Color[" + i + "] = " + colors[i]); } Log.i(FORECAST, "MAXVALUE: " + maxContour + ";MINVALUE: " + minContour); ClassBreaksRenderer classBreaksRenderer = createClassBreaksRenderer(colors, maxContour, minContour); classBreaksRenderer.setField("Contour"); classBreaksRenderer.setMinValue(0.0); contourLineGraphicsLayer.setRenderer(classBreaksRenderer); Log.i(FORECAST, "Create Graphics Completed"); Log.i(FORECAST, "Add Graphics to MapLayer"); getMapView().post(new Runnable() { public void run() { // TODO Auto-generated method stub getMapView().addLayer(contourLineGraphicsLayer); if (MainActivity.dialog.isShowing()) { MainActivity.dialog.dismiss(); } Log.i(FORECAST, "Add Graphics to map layer Completed"); } }); } else { Log.i(FORECAST, "GP failed"); } checkStatusTimer.cancel(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); if (MainActivity.dialog.isShowing()) { MainActivity.dialog.dismiss(); } checkStatusTimer.cancel(); } } }, 1000, 2000); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); if (MainActivity.dialog.isShowing()) { MainActivity.dialog.dismiss(); } } }
到此关于GP服务的准备过程所有完成,下面咱们开始编写调用的代码,有了上面的工做,下面的就很容易了:
首先咱们看一下Android界面的布局:
这里加载本地数据为预留功能。
而后咱们实例化一个GPService对象,传入自动绘制预测点要素集所需的起点坐标和MapView对象,而后设置等值线间距和平滑容差,最后调用GP服务便可。代码以下:
if (current_Point == null) { Toast.makeText(MainActivity.this, "请先在地图上添加计算计算原点", Toast.LENGTH_LONG).show(); return; } EditText intervalEditText=(EditText)MainActivity.this.findViewById(R.id.interval_value_EditText); EditText toleranceEditText=(EditText)MainActivity.this.findViewById(R.id.smooth_value_EditText); if(Double.parseDouble(intervalEditText.getText().toString())<=0){ Toast.makeText(MainActivity.this, "等值线间隔必须大于0", Toast.LENGTH_LONG).show(); return; } if(Double.parseDouble(toleranceEditText.getText().toString())<=0){ Toast.makeText(MainActivity.this, "平滑容差必须大于0,且注意单位是米", Toast.LENGTH_LONG).show(); return; } GPService gpservice = new GPService(current_Point.getX(), current_Point.getY(), mMapView); gpservice.setContourIntervalValue(Double.parseDouble(intervalEditText.getText().toString())); gpservice.setSmoothToleranceValue(Double.parseDouble(toleranceEditText.getText().toString()), "esriMeters"); dialog= ProgressDialog.show(MainActivity.this, "绘制等值线...", "正在请求GIS服务....請稍後!"); gpservice.StartGPService();
最后等待咱们的等值线出来吧。
最后的效果图:
总结:
关于GP服务,实际上不管Java平台仍是C#平台,不管Silverlight,Flex仍是JavaScript,不管Window Phone 仍是Android(IOS没有了解过,不是很清楚,屌丝没有苹果机真啊....)他们调用GP服务的过程基本上都是同样同样的,注意是基本上。所以多少仍是有些差异,可是整体上来讲都遵循这样的过程:
1.声明Geoprocessor变量,指定GP服务地址。
2.根据GP服务,声明其所需的参数,并添加到GPParameter集合(其余平台名字也行稍有不同)
3.根据异步和同步,执行调用GP服务的方法
4.获取GP服务的结果
了解了这个过程那么您就知道GP服务该如何使用了。
一点题外话:
再次写博客,发现上一次仍是一年之前,不禁的感慨时间飞逝,这一年中,浑浑噩噩的感受就过去了,而后匆匆忙忙的找了份工做,而后立刻就要毕业了...留给本身的时间也已再也不太多,听不少工做的朋友说工做了就身不禁己了,甚至连本身的时间也不是本身的啦,因此我想趁着本身尚未被剥削本身,干点本身想干的事情...
一年了,说长不长,说短不短,失去了不少,也收获了不少,我想至少博客园收获了100个粉丝,虽然很少,可是却也是一笔不小的财富,或者这是笔者有生以来最大的粉丝群了。所以我想既然写了这个博客,那么就得继续努力,就得坚持。
最后感谢您能读完这篇博文,由于它确实长了点,可是仍是但愿您能有所收获,但愿或多或少能帮助您解决遇到的问题,也但愿您继续关注个人博客,若是您以为好就点个赞,有什么建议也欢迎您留言一块儿讨论...
欢迎转载,版权全部:博客园,Mr|Right