arcserver访问sde的数据,执行相交分析,并将分析后的结果保存到sde中。最后使用ArcGIS API JavaScript在前端调用。

需求:在arcgis server中发布一个叠加分析GP服务,然后基于arcgis javascript api接口,调用发布的gp服务,实现两个图层进行相交叠加分析的功能,分析的源数据和结果数据均存储到oracle 11g中,叠加分析完成后,将结果图层添加到地图容器中进行叠加显示。(详细点就是,在前端通过输入两个图层的名字,然后就可以访问SDE的两个要素类进行相交分析,然后再输入结果图层的名字便可以把结果这个新的要素类保存在SDE中)

在本次研究中,真可谓是时而停杯投箸不能食,拔剑四顾心茫然;时而众里寻他千百度。蓦然回首,那人却在,灯火阑珊处;时而山重水复疑无路,柳暗花明又一村。这应该就是就是每个程序员每天的心境吧。本次研究花了我近4天的时间,不过最终解决。下面我将自己遇见的坎坷和弯路写下来,一者作为笔记帮助记忆,二者可供那些和我有同样需求的人参考。由于本人水平有限,文中可能有不得体的地方,还望各位大神多多指点。好了,不多比比,开始分享我的坎坷之旅。

思路:构建GP模型----->发布GP服务和ArcServer测试----->前端调用

准备工作:1.ArcGIS系列软件ArcGIS Desktop10.2 ,ArcGIS Server 10.2(网上很多资源和教程) 

                   2.Oracle 11g 64位服务器端,Oracle 11g 32位客户端 (网上也有很多资源和教程,安装起来出了点麻烦,不过最后已解决)

                   3.ArcGIS API JavaScript3.17离线配置(网上很多资源和教程)

                   4.在ArcMap中通过SDE进行Oracle数据库连接,然后在SDE里新建两个面要素类用来测试。(网上也有很多教程,不再赘述)

一.构建模型

既然要发布GP服务,那么构建GP模型是少不了的(构建模型有两种,第一是直接使用ArcGIS的ArcTool进行拖拉,第二是用python写ArcPy。本次需求只需要进行拖拉ArcTool构建模型即可,不过在研究过程中有不少网友建议使用ArcPy,现在觉得他们是不太懂了)。

坎坷一:首先创建空白模型:mytoolbox--->toolbox--->model。将ArcToolBox的Analysis Tool--->overlay--->Intersect拖拉到模型中,获取参数inputFeature。这个时候一看傻眼了,因为这个inputFeature的类型是个值表(即可以添加多个要素类),而我的需求是两个,并且需要通过自己定义名字,怎么办?这个时候我做了一种尝试,就是添加两个要素类直接发布,然后添加模型参数p,发布,但在发布的时候发现这个inputFeature的类型是常量,而不能是自定义,(不再贴图,可自行尝试)那就意味着前端调用的时候只能做发布服务时候的两个要素类的相交分析,这简直就是扯淡,所以直接放弃。

坎坷一的解决:这个时候要做三步工作。

1.在创建的模型中,点击insert--->create varible--->string,即创建一个类型为字符串的变量,由于我需要添加两个要素类,故创建三个变量(两个用于输入,一个用于输出)。

2.双击,将SDE内的两个要素类名字分别填写到两个变量中(即这个变量的是通过赋SDE的要素类的名字来和SDE的要素类进行连接的),将要输出的要素类的名字写在第三个变量中。然后给三个变量命名一下(这个命名是在模型的inputFeature里调用,我用的是LayerName1,LayerName2,LayerName3)。

3.双击inputFeature,将SDE的路径写在input里,最后写上%变量的名字%。同样双击out,输入                                                 数据库连接\myOracle.sde\%Layername3%,数据库连接\myOracle.sde是你SDE的路径,layerName3就是调用你输出的要素类的名字。(心细的人可能发现一个是LayerName1,一个是Layername1,这里不分大小写,所以不必在意,不过最好还是严格一点)。然后就是在三个变量加上模型参数。如图所示:

arcserver访问sde的数据,执行相交分析,并将分析后的结果保存到sde中。最后使用ArcGIS API JavaScript在前端调用。

注意:在这个过程中,有很多人强调模型环境变量的临时工作空间的重要性,其实临时工作空间如果设置为  数据库连接,那么在input和out里可以填写%scratchworkspace%/SDE的名字/%变量名字%是可以的,这就是个相对路径,%scratchworkspace%取的是你的临时工作空间而已。但是发布服务的时候会提示输入错误,在网上看一篇博客说是在input里一定要填写绝对路径,不然本地可以访问相对路径,在服务器就不能访问了。而我的临时工作空间设置的是默认数据库,也没见出什么错。

感觉差不多了,那就发布服务,发布GP服务网上很多,也就不再赘述。(建议在发布的时候选择异步方式,勾选查看含地图服务的结果,这样可以在测试的时候能看到地图。不然的话就是一堆json,看起来很不直观)发布后就开始在server上测试吧,这个时候会发现出现错误,错误为执行成功,输出失败,应该是这个%LayerName3%出问题了。

二.发布服务和ArcServer测试

坎坷二:现在捋捋思路,服务在server上能够执行成功,但是遇见%LayerName3%输出失败,那就索性在发布服务的时候直接把输出要素类的名字写死,再进行尝试。发现结果是成功的,这个时候可以在SDE里多建几个要素类,然后在server上通过输入不同的要素类的名字,来得到不同的分析结果。但是在ArcMap里打开我的SDE,并没有发现有新的要素类。

那么问题来了:我现在又两个问题还没有解决,第一,我是要在前端通过输入结果要素类的名字来得到这个结果。第二,我还没有把结果存储在SDE中。这个时候打开C:\arcgisserver\directories\arcgisjobs,发现在Server上处理的中间数据都在这个地方存储,(这个位置是ArcServer测试默认存储的路径,在创建manger的时候可以进行更改),并且我把这些数据连接到arcmap里都可以打开。

坎坷二的解决方法:对于第二个问题,我可以在输出结果的地方再连接一个featureClass to geoDataBase工具,然后这个时候out的存储路径就随便了,因为是默认数据,在最后输出的地方  路径依然是   数据库连接\myOracle.sde\%Layername3%。这个时候发布服务,测试依然有错误。最后我的模型如下:

arcserver访问sde的数据,执行相交分析,并将分析后的结果保存到sde中。最后使用ArcGIS API JavaScript在前端调用。

这个时候再捋捋思路,忽然发现,自己并没有将SDE这个数据库注册到ArcAserver里,以至于ArcServer无法访问数据库的位置。那么就抱着最后一丝希望,在ArcServer上注册企业级数据库(网上也有教程,不再赘述。方法有二,一在server上直接注册,二在发布服务的时候点击分析,出现警告没有注册数据库,双击就可以注册数据库了)。这个时候再在Server上测试,竟然发现并没有报错,在ArcMap里打开SDE,发现也出现了一个新的要素类,并且命名和ArcServer测试的时候输入的一样。

卧槽,就这样成功了!(不要心急,还得在前端调用呢。。。。。)

三.前端调用

在调用前发现了一个问题,就是我做相交分析,肯定得需要将分析的要素类可视化吧(小意思,发布服务就可以调用进行可视化了),但我还要生成新的要素类,这个也要进行可视化吧。那么问题来了,总不能执行一次,就要将结果发布为服务一次吧。。。。那这个交互性也太差了吧。。这个时候忽然发现上周研究的动态图层就起到作用了,动态图层网上教程一大堆,不再赘述,不过提醒各位在启用动态图层的时候,最好在发布服务的时候(这个发布服务是指动态图层只需要发布一次服务,然后便可访问文件夹、地理数据库或者企业级数据库的所有要素类)启动,这时候有个浏览路径的选项,不然在server上启动就没有这个浏览选项,而是复制路径,很容易出错。(我之前在这跌了个大跟头,一直报错,但不知错从何来,原来是这个路径的问题)

本人在前端设置的界面如下(比较low,不过凑合能用吧)

arcserver访问sde的数据,执行相交分析,并将分析后的结果保存到sde中。最后使用ArcGIS API JavaScript在前端调用。

这个界面的布置代码我就不再粘上来了,相信任何一个优点前端基础的人都是so easy。

那么直接上代码:

     intersectFunction:function (map) {
            //点击叠加分析,图层选择框显示
            dom.byId("intersectTool").onclick=function () {
                dom.byId("selectBoxContainerDiv").style.display="block";
            };
            //点击X,图层选择框消失
            dom.byId("selectBoxClose").onclick=function () {
                dom.byId("selectBoxContainerDiv").style.display="none";
            };
            //执行图层选择框拖拉函数
            dragFunction(dom.byId("selectBoxHeaderDiv"),dom.byId("selectBoxContainerDiv"));
            //动态图层获取图层函数,参数为需要添加的要素类的名字
            function getLayerFunction(OracleLayerName){
                //定义一个数据源
                var dataSource=new TableDataSource();
                //此处为我们设置的命名空间
                dataSource.workspaceId="MyDatabaseWorkspaceID";
                //命名空间下面的要素类
                dataSource.dataSourceName=OracleLayerName;
                //定义一个图层数据源
                var layerSource=new LayerDataSource();
                //给图层数据源赋值
                layerSource.dataSource=dataSource;
                //定义一个要素图层:注意链接为动态图层的地址
                var getLayer= new FeatureLayer("http://192.168.220.132:6080/arcgis/rest/services/test1/MapServer/dynamicLayer", {
                    mode: FeatureLayer.MODE_ONDEMAND,
                    outFields: ["*"],
                    source: layerSource
                });
                var lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0, 255, 0]), 2);
                var fillSymbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, lineSymbol, new Color([0,255,0]));
                //定义渲染器
                var renderer = new SimpleRenderer(fillSymbol);
                getLayer.setRenderer(renderer);
                getLayer.refresh();
                return getLayer;
            };
            //添加和移除图层函数
            function addAndRemoveFunction(){
                //获取添加的a标签
                var aAdd=document.getElementsByClassName("add");
                //获取移除的a标签
                var aRemove=document.getElementsByClassName("remove");
                for(var i=0;i<aAdd.length;i++)
                {
                    aAdd[i].index=i; //为每个添加标签添加一个index属性,并赋值为i
                    aAdd[i].onclick=function () { //点击添加标签
                        //获取到该标签的父亲的第二个子标签,即textbox标签的value
                        var  currentLayer=getLayerFunction(this.parentNode.children[1].value);
                        map.addLayer(currentLayer);//将该图层添加到地图
 //当点击移除标签时,这个this.index指的是点击的位置,即,this代表点击的添加标签,index是指第几个
                        aRemove[this.index].onclick=function () {
                            map.removeLayer(currentLayer);
                        }
                    };
                }
            };
            addAndRemoveFunction();
            //点击确定按钮事件
            dom.byId("determine").onclick=function () {
                //定义GP服务对象
                var intersect=new Geoprocessor("http://192.168.220.132:6080/arcgis/rest/services/GPtest/intersect_gptest/GPServer/intersect")
                //定义参数,是个json,里面的字段要和发布在arcserver的字段相对应
                var gpParams={
                    "LyName1":dom.byId("layer1").value,
                    "LyName2":dom.byId("layer2").value,
                    "LyName3":dom.byId("layer3").value
                };
                //执行GP服务,第一个参数是json,第二个是回调函数
                intersect.submitJob(gpParams,showResultFunction);
                //执行回调函数
                function showResultFunction() {
                    alert("成功");
                    //执行成功,则执行添加或移除函数
                    addAndRemoveFunction();
                }
            }
        }

这个代码大家只能做个参考,不能直接调用,因为这是我写在define里的函数,而不是写在require里的。

花了两个多小时,终于把这几天遇见的问题给写完了。真是痛并快乐着。若发现不当之处,希望大神们多多批评与指点,也希望大家能够一起互动,一起交流,一起进步,一起成长。谢谢。