ArcGIS Server 9.2 for Java 讲座

opgaoyuan 贡献于2011-07-11

作者 *  创建于2008-03-23 08:28:00   修改者USER  修改于2011-07-05 09:11:00字数81102

文档摘要:在正式开讲之前,我们来探讨一下为什么要使用JSF来实现SERVER ADF,这也是很多开发商跟我讨论时经常会问到的问题。世界上有300种Java的framework,这个数字是我几个月前听到的,现在可能已经不止这个数了。我曾经用过的framework包括Structs,Hibernate,Spring。这些framework不但能加速开发,而且会带来全新的程序设计理念。我当时的感觉是:原来程序还可以这样写! 当然,这些framework都是开源免费,你不用担心自己的良心受到谴责,也不用担心比尔盖茨半夜敲门收钱。 在这300多种framework中,ESRI为什么选择了JSF?各个framework有各个framework的好处。JSF最大的优点就是:像 Swing 和 AWT 一样,JSF 是一个可以提供一组标准的、可重用的 GUI 组件的开发框架。
关键词:

 ArcGIS Server 9.2 for Java 讲座 1、前言------为什么我们选择JSF,而不是其它framework 1 2、ArcGIS Server Java开发 JSF基础 2 3、ArcGIS Server Java ADF开发的JSF知识介绍 5 4、ArcGIS Server 开发初步 -- 自定义工具 7 5、使用图片和TrueType字体进行标注 10 6、Server Java讲座-----扩展Tasks框架 13 7、Server Java 开发实战---自定义command 19 8、 ArcGIS Server Java 讲座--ADF 体系结构 第一部分 21 9、ArcGIS Server JAVA讲座--实现后台JSF Bean和前台Javascript的联动 24 10、ArcGIS Server JAVA讲座 --AJAX基础 25 11、ArcGIS Server JAVA开发讲座--- ADF中的Ajax函数 27 12、ArcGIS Server Java讲座--如何在用JSF在服务器端处理Ajax请求 29 13、ArcGIS Server Java讲座----利用Ajax得到当前地图的比例尺(服务器端) 32 14、ArcGIS Server Java讲座--用Ajax得到地图比例尺(客户端代码) 33 15、ArcGIS Server JAVA 讲座 自定义Functionality 35 16、ArcGIS Server Java讲座---用自定义functionality实现用户权限控制 37 17、Server Java讲座-----扩展Tasks框架 42 18、ArcGIS Server Java讲座--自定义Tools开发 48 19、ArcGIS Server Java讲座-空间查询和高亮显示的实现 52 20、Server Java 自定义开发-Network Analysis 54 21、ArcGIS Server开发——控制图层是否可见 59 22、航线查询问题-Server Java的实现方法 59 23、ArcGis 航线查询完全例子 64 24、ArcGis Server 中 如何在Task中实现 下拉列表和 checkbox 和 radiobutton.(完整例子) 73 25、ArcGIS Server开发——标注 74 26、ArcGIS Server Java开发--Born for SOA系列 前言 75 27、ArcGIS Server Java开发--Born for SOA系列 Web Service基础 75 28、ArcGIS Server Java讲座:Born For SOA--Server对于SOAP的支持 81 29、Server Java开发--Born for SOA系列 通过代理类访问ArcGIS Server Web Service 84 1、前言------为什么我们选择JSF,而不是其它framework 在正式开讲之前,我们来探讨一下为什么要使用JSF来实现SERVER ADF,这也是很多开发商跟我讨论时经常会问到的问题。       世界上有300种Java的framework,这个数字是我几个月前听到的,现在可能已经不止这个数了。我曾经用过的framework包括Structs,Hibernate,Spring。这些framework不但能加速开发,而且会带来全新的程序设计理念。我当时的感觉是:原来程序还可以这样写! 当然,这些framework都是开源免费,你不用担心自己的良心受到谴责,也不用担心比尔盖茨半夜敲门收钱。        在这300多种framework中,ESRI为什么选择了JSF?各个framework有各个framework的好处。JSF最大的优点就是:像 Swing 和 AWT 一样,JSF 是一个可以提供一组标准的、可重用的 GUI 组件的开发框架。 原因之一: 行为与表示的完全分离。      行为和表现不分离的一个典型例子是普通的JSP 技术,这一技术很好,因为很容易在 HTML(和类 HTML)页面中混合 Java 代码。这样我们就可以使用强大的Java API的功能。当然,很明显的坏处是行为表现黏合在一起。而且代码不容易重用。这让我想起刚使用JSP技术时,在文本编辑器下写JSP页面,读写数据库,并且在同一个页面上显示出来的痛苦回忆。       Model1架构的出现使开发人员可以用javabean的方式进行开发,舒服多了。当然,还有更好的。       这就不得不提到著名的MVC结构,(MVC) model-view-controller架构提供了一系列的设计模式,这些模式让你可以轻松地应用于程序地开发,特别是有界面程序的开发。现在又有多少的程序是没有界面的呢?          Model包装了商业逻辑,也包含了数据持久的代码,包括存取数据库或者存取文件。Model最好不要跟View有任何关系,它最好不要知道谁将显示结果,是一个Swing界面,还是JSF界面,还是普通的JSP界面。为了做到这一点,事先的规划要花一些时间,但是相信我,这些花的时间带来的价值将超过你的想象。如果设计得当,许多用ArcGIS Engine开发的代码可以轻松在Server中使用,不需要修改任何代码。.net也是一样。       View只用来显示显示逻辑,不能有任何商务逻辑。controller是view和model的桥梁,它和model进行交互,并且把结果显示在Model中。显示页面的选择也由controller进行选择。我会在后面的讲座中详细解释JSF中的Controller的实现。        Structs,Spring都实现了MVC的架构。但是它们都没有像JSF这样,提供一个事件驱动的组件对象模型。大家可以想一想,一个平常在HTML中使用的标签,     ,在JSF框架中变成了一个组件,这个组件还可以有事件,而且可以对这个事件可以进行编程。这是多么得激动人心啊! GIS程序需要和用户进行良好地交互,选择一个事件驱动的framework,可以更加容易编程和实现。     原因之二: 事件容易捆绑到服务器端代码。 JSF提供了多个事件驱动的GUI控件, 比如最最常见的command button。这些command button会产生事件,我们有机会对事件进行编程;我在以前的讲座中曾经讲过,ArcGIS Server的MapControl和command button是兄弟,他们都继承自javax.faces.component.UICommand;是不是感觉很奇妙啊?继承自UICommand的原因是要利用UICommand的很多事件,对鼠标在Map Control上的动作,比如放大,缩小,需要产生事件,这些事件提交到相应的监听器,监听器会对事件进行处理。有关于MapControl的问题先提这些,在后面的讲座中我会详细解释。          举一个简单的例子吧,一个最简单不过的fullextent工具,如果我自己来写,可以写成这样: 这个按钮,用来缩放到全图,它的监听器是绑定到getMapBean的getFullMap方法,在该方法里面,我会将地图的大小缩放到全图范围。代码我会稍后贴上来。这就是JSF的事件捆绑到服务器代码的功能。普通的一个button,点击事件可以映射到后台Bean的某一个方法,在后台方法里面执行商业逻辑,进行数据存取。这个概念跟普通的Swing程序的事件和监听器机制十分类似,Java程序员们看了肯定倍感亲切。注意,刚才这个button没有利用任何ADF的类,在我们的ADF中,所有要建大厦的材料都已经给您准备好,fullExtent功能只需要三行代码即可实现。 事件容易捆绑到服务器端代码,这是ESRI选择JSF作为我们ADF底层的另外一个重要的原因。 2、ArcGIS Server Java开发 JSF基础 有很多开发人员问我,如果我已经有了一个JSP的Application,为了使用JSF,为了使用我们新版的ArcGIS Server的Java ADF,我需要重新修改写我的程序以让它们适应整个JSF框架么?这是一个很有意思的问题,我可以想象出大家头皮发麻,左右为难的感觉,这也引出了今天的话题,Servlet容器是如何对JSF的程序进行处理的呢? 随便找一个JSF的Application,可以是我们的ArcGIS Server的Sample,也可以是任何您从网上下载的JSF例子,打开web.xml文件,你就可以看到该文件里面有如下的servlet映射: Faces Servlet javax.faces.webapp.FacesServlet 1 Faces Servlet *.jsf 这是Java程序员最熟悉不过的Servlet配置了,该Servlet映射是什么意思呢?将所有对后缀名为jsf的请求,都交由javax.faces.webapp.FacesServlet来处理。哦,另外插一句,JSF的标准是公开的,如果你觉得这个FacesServlet不好,你大可以自己写一个JSF处理Servlet。将所有对jsf的请求自己来处理。事实上,已经有人这么做了,这就是Myfaces,可以参考Apache的网站。 言归正转,如何将JSF的应用集成到已经有的应用程序中呢,您一定已经想到了。事实上事情很简单,在访问某个JSF页面的时候,映射到某个目录即可。这个目录在web.xml进行了设置,比如下面的配置就可以把所有的对faces的请求,让Face Servlet来处理: Faces Servlet /faces/* 所以,如果你在根目录了放了一个hello.jsp页面,这个jsp页面使用了JSF框架,那么你在访问这个jsp页面时用http://servername/faces/hello.jsp就可以了,你的容器会把这样的请求提交Face Servlet来处理,这个时候,就轮到Face Servlet来大显身手了,它会忠实地执行你的请求。 JSF的Managed Bean是保存在session中的,所以你在JSF页面和普通JSP之间切换页面的时候,不会丢失信息,反过来也一样。JSF自己有自己的Page Navigation系统,但是它仍然可以跟普通的JSP页面互相切换,我们在后面的讲座中会讲到。 所以如果你已经存在的一个项目,想上我们的ArcGIS Server,利用其基于JSF的ADF进行开发,也是完全可以的。当然,新的应用最好用JSF框架开发,开始时可能需要一点时间去学习,对于你后面会用到的强大的JSF的功能,这些投资是完全值得的。 欢迎大家进入美妙的JSF的世界。 了解了Servlet是如何处理你的JSF请求的,那么接下去我们来看看,让JSF程序跑起来需要什么必要条件,一个Servlet容器是必需的,这个一般取决于公司给我们配什么,或者客户要求什么。一般免费的好选择是Tomcat,价格便宜量又足,我们大家都用它。当然如果项目大,经费充足,IBM的J2EE容器Websphere和Bea公司的 Weblogic也是一个好选择。选用大公司J2EE容器有一个好处,即使三更或者五更出了问题,你照样可以拍着桌子让他们的工程师过来帮你解决问题,如果你用我们ESRI的软件,你也可以这样;而用免费的软件三更出了问题只有我们自己出马了,当然,google和百度在这个时候看在点击率的份上,还是可以拉我们一把的。除了一个好的容器,还需要一个JSF的实现,用SUN的Reference Implementation总是不会错的,人家是标准的制定者嘛,名字也起做“参考实现”,写来让你参考的。也可以选择功能更加强大的Myfaces。ESRI的ArcGIS Server ADF遵循标准的JSF,所以ADF可以跑在Sun 的JSF实现,或者Myfaces上。我们这次所有的教程都是基于sun的标准的JSF实现。还有其它的JSF实现,大家可以根据自己的爱好,择优选择之。如果你觉得都不好用,大可以参考标准自己写一个实现,除了你的老板(要投入更多的开发经费)和你的team (要干更多的活) ,是没有人有意见的。 如果您像我一样选择了SUN的参考实现,那么去SUN的网站上去下载一个JSF的开发包吧,如果不想下载,随便找一个Server Java ADF的例子,在WEB-INF/lib目录里面有JSF的包,名为jsf-api.jar和jsf-impl.jar的两个包就是;注意那个JSP的标准标签库jstl.jar,把它跟jsf的包放在一起,你不一定用标准标签库,但是JSF用到了它。 我把JSF的doc放在这里给大家下载,因为这个东西实在不好找,有人知道好的下载URL,可以在这里贴一个。 把这些包放在一个web application的lib目录里面有,你的程序你可以使用JSF的强大功能了。让我也来俗气一下,写一个简单的hello world作为今天的结束吧。注意在你的web application里面的web.xml配置文件添加了Face Servlet的声明和URI映射。 <%@ page c%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 欢迎来到ArcGIS Server ADF 世界

保存后访问Http://servername:port/faces/hello.jsp即可,如果页面能够出来,那么恭喜您,您已经迈入了JSF wonderland的大门。 3、ArcGIS Server Java ADF开发的JSF知识介绍 ArcGIS Server 的Java ADF 开发使用到了JSF的知识,下面结合ArcGIS Server的开发,介绍一下JSF的知识 Struts和JSF/Tapestry都属于表现层框架,这两种分属不同性质的框架,后者是一种事件驱动型的组件模型,而Struts只是单纯的MVC模式框架。 首先事件是指从客户端页面(浏览器)由用户操作触发的事件,Struts使用Action来接受浏览器表单提交的事件,这里使用了Command模式,每个继承Action的子类都必须实现一个方法execute。 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。 struts重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以转弯实现的。 而这种情况使用JSF就可以方便实现,    #{login.userId}表示从名为login的JavaBean的getUserId获得的结果,这个功能使用struts也可以实现,name="login" property="userId" 关键是第二行,这里表示如果userId的值改变并且确定提交后,将触发调用类UserLoginChanged的processValueChanged(...)方法。 JSF可以为组件提供两种事件:Value Changed和 Action.相当于struts中表单提交Action机制,它的JSF写法如下:    从代码可以看出,这两种事件是通过Listerner这样观察者模式贴在具体组件字段上的,而Struts此类事件是原始的一种表单提交Submit触发机制。如果说前者比较语言化(编程语言习惯做法类似Swing编程);后者是属于WEB化,因为它是来自Html表单。 基本配置 Struts和JSF都是一种框架,JSF必须需要两种包JSF核心包、JSTL包(标签库),此外,JSF还将使用到Apache项目的一些commons包,这些Apache包只要部署在你的服务器中既可。 JSF包 下载地址:http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation。 JSTL包下载在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi 所以,从JSF的驱动包组成看,其开源基因也占据很大的比重,JSF是一个SUN伙伴们工业标准和开源之间的一个混血儿。 上述两个地址下载的jar合并在一起就是JSF所需要的全部驱动包了。在ArcGIS Server的项目下的lib目录下有相关的库文件如下:commons-beanutils.jar commons-collections.jar commons-digester.jar commons-lang-2.0.jar commons-logging.jar jsf-api.jar jsf-impl.jar jstl.jar standard.jar 与Struts的驱动包一样,这些驱动包必须位于Web项目的WEB-INF/lib,和Struts一样的是也必须在web.xml中有如下配置:        Faces Servlet     javax.faces.webapp.FacesServlet     1           Faces Servlet     *.faces    这里和Struts的web.xml配置相似,简直一模一样。 正如Struts的struts-config.xml一样,JSF也有类似的faces-config.xml配置文件:        /index.jsp            login       /welcome.jsp                user     com.corejsf.UserBean     session    在Struts-config.xml中有ActionForm Action以及Jsp之间的流程关系,在faces-config.xml中,也有这样的流程,我们具体解释一下Navigation: 举例:如果在mapviewer.jsp中有一个事件: action的值必须匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一个login事件,那么事件触发后下一个页面将是welcome.jsp JSF有一个独立的事件发生和页面导航的流程安排,这个思路比struts要非常清晰。 UI界面 JSF和Struts一样,除了JavaBeans类之外,还有页面表现元素,都是是使用标签完成的,Struts也提供了struts-faces.tld标签库向JSF过渡。 使用Struts标签库编程复杂页面时,一个最大问题是会大量使用logic标签,这个logic如同if语句,一旦写起来,搞的JSP页面象俄罗斯方块一样,但是使用JSF标签就简洁优美: 如果authenticationBean中inboxAuthorized返回是假,那么这一行标签就不用显示,多干净利索! 缺点: JSF这类框架面临的最大问题,它大量的使用了标签库,目前流行的网页制作工具(如deamweaver)又没有提供足够的支持,所以只能依靠挖掘dataTable标签的各个属性,并且大量依赖css才能实现页面的美化。如果java世界能有一个强大的JSF IDE,能够提供vs.net一样的能力,那么JSF也许会更容易流行。 4、ArcGIS Server 开发初步 -- 自定义工具 在Server生成的Web App中,页面的工具按钮可以分为两类: l命令(Command):A command is an element on a JSP page that triggers a server side action without any further interaction on the client. An example of a command in the sample application is the "zoom to full extent" button. Once the user clicks the button, a method is called on the server。不与用户通过界面交互。 l工具(Tool):A tool has further client side interaction before calling a method on the server. An example of a tool in this application is "zoom to rectangle". Once the user clicks the button, drags a rectangle over the map indicating the area they want to zoom to, and then a method is called on the server。与用户通过界面交互。 一、继承接口 public Interface com.esri.adf.web.faces.event.MapToolAction{ void execute(MapEvent event); } lMapToolAction 接口代表由MapControl控件事件所激活的服务器端工具,系统已预设继承此接口的类: PanToolAction(平移), ZoomInToolAction(放大), ZoomOutToolAction(缩小) lMapControl 创建MapEvent 事件并将其传给继承接口的工具类的 execute(MapEvent) 函数,The business logic for the tool should be implemented in this method according to the event。 二、工具在JSP页面上的tag表达如下: 三、注册managed-bean将所写的类作为一个managed-bean注册到faces-config.xml,并用WebContext实例作为其初始化参数: ToolClass com.brsc.MyToolClass request webContext #{mapContext} 四、注释: 1. JSP的Tag中serverAction写入继承MapToolAction接口的类(全称),代表对于此工具服务器端要进行的操作[ execute(MapEvent event)] 用户也可以使用任何Managed Bean的函数作为工具对应的方法,只要这个函数使用如下声明: public void anyMethodName(MapEvent event) JSP标签使用serverMethod ,如下: 这样,MapControl也会将适当的MapEvent 事件传入此函数。 2. JSP的Tag中clientAction写入客户端鼠标选择的方式: EsriMapPoint 点选 EsriMapLine 线 EsriMapRectangle 四边形 EsriMapCircle 圆 EsriMapOval 椭圆 EsriMapPolyline 多线 EsriMapPolygon 多边形 EsriMapPan 移动 对应Server端的几何形状(附图) 3. MapEvent代表客户端进行操作产生的事件,一般会用到MapEvent的 public WebGeometry getWebGeometry()函数来得到客户端输入的几何形状 //Returns the WebGeometry in screen coordinates corresponding to //the client action performed by the user. 来获得客户端产生的形状,这些Geomentry一般都是screen坐标,需要用toMapGeometry(WebMap)转换为 地图坐标 。 一般操作如下: public void myToolMethod(MapEvent event) { WebContext ctx = event.getWebContext(); WebGeometry screenGeom = event.getWebGeometry(); WebGeometry mapGeom = screen.toMapGeometry(ctx.getWebMap()); ... } 4. JSP的Tag中clientPostBack l 设置为false,刷新地图,并且刷新页面; l 设置为true,只刷新地图,不刷新页面; 5、使用图片和TrueType字体进行标注 直奔主题吧,接上个主题的讲座的内容,讲讲如何用图片进行标注。在前面一个讲座我们已经说明,如何对选中的物体进行高亮显示。而且这一部分工作是在ADF这一端完成的。那么有的时候,我们进行高亮显示的时候,不仅仅希望只是设置颜色,我们希望能够用图片或者truetype字体进行标注。比如在犯罪地点放一个坏人之类的功能。 我们来看看实现原理,关键是两个类,WebTrueTypeMarkerSymbol和WebPictureMarkerSymbol,没啥好说的,直接用代码来说明吧: 先来看看普通的点标注: WebPointpt=(WebPoint)arg0.getWebGeometry().toMapGeometry(arg0.getWebContext().getWebMap()); WebSimpleMarkerSymbol markers =null; markers = new WebSimpleMarkerSymbol(); markers.setAntialiasing(true); markers.setColor("255,0,0"); markers.setWidth(8); markers.setOutlineColor("255,0,0"); markers.setMarkerType(WebSimpleMarkerSymbol.CIRCLE);   markers.setPicture(bytInput); GraphicElement ge=new GraphicElement(); ge.setGeometry(pt); ge.setSymbol(markers); WebGraphicsgraphics=arg0.getWebContext().getWebGraphics(); graphics.addGraphics(ge); arg0.getWebContext().refresh(); 注意webgraphicsymbol的setPicture方法的参数不是图片目录,而是图片的二进制数组,所以需要用文件IO把图片读取进来。当然,如果用户访问量很大,线程就不安全了,大家可以在application启动时进行读取,放在context的某个attribute里面。我原来认为是通过设置路径方式实现,这样又可能可以搞定gif图形的闪烁,但是现在实验结果是不行。设置图片标注的代码如下: WebPointpt=(WebPoint)arg0.getWebGeometry().toMapGeometry(arg0.getWebContext().getWebMap()); //图片在servelet容器里面目录                               String picPath=FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath()+"\\images\\angle.gif"; File myFile = new File(picPath);         FileInputStream myStream= newFileInputStream(myFile);    BufferedInputStream buf = newBufferedInputStream(myStream); byte[] bytInput = newbyte[(int)myFile.length()]; buf.read(bytInput, 0, (int) myFile.length()); buf.close(); myStream.close(); WebPictureMarkerSymbol markers=newWebPictureMarkerSymbol(); markers.setPicture(bytInput); GraphicElement ge=new GraphicElement(); ge.setGeometry(pt); ge.setSymbol(markers); WebGraphics graphics=arg0.getWebContext().getWebGraphics(); graphics.addGraphics(ge); arg0 .getWebContext().refresh(); 在jsf文件里面添加如下代码,调用图片标注工具,进行测试: 图片标注的结果如下: 用图片做markSymbol 我们也可以使用TrueType字体里面的矢量字体对图形进行符号化显示,这也是我们经常使用的方法,具体代码如下: webMap=arg0.getWebContext().getWebMap(); WebPointpt=(WebPoint)arg0.getWebGeometry().toMapGeometry(webMap); WebTrueTypeMarkerSymbol trueMarkerSymbol=new WebTrueTypeMarkerSymbol(); //注意使用系统里面已经安装的字体 trueMarkerSymbol.setFontName("ESRITransportation & Civic");//设置索引 trueMarkerSymbol.setCharacterIndex(8); trueMarkerSymbol.setFontColor("255,0,0"); trueMarkerSymbol.setFontSize(20); trueMarkerSymbol.setFontStyle(WebTrueTypeMarkerSymbol.BOLD);GraphicElement ge=new GraphicElement(); ge.setGeometry(pt); ge.setSymbol(trueMarkerSymbol);WebGraphicsgraphics=arg0.getWebContext().getWebGraphics(); graphics.addGraphics(ge);arg0.getWebContext().refresh(); 把上面的代码放在一个点击工具里面,在jsf文件中用如下代码进行调试:    TrueType字体标注的结果如下图所示:     6、Server Java讲座-----扩展Tasks框架 Tasks框架是一个很有意思的东西。如果你对Tasks框架还没有概念,你可以参考一下web map application那个Sample,左边浮动的放大工具,查询工具都是用Tasks框架构成。有人会问,既然已经有了command和tools,为什么还需要tasks呢?事实上tasks的目的是不同的;我们可以看一下web map application,可以看到,相同类型的button和toos组合在一起,组成一个逻辑单元,这就是tasks的选择条件。 我们来看看如何编写Tasks,编写Tasks是非常有意思的事情。Tasks跟tools一样,也是一个普通的Java类即可,这个Java类可以从Object直接继承。请看下面的Task的例子: 1public class JSTasks { 2public void doSomething(){ 3System.out.println("Task button clicked."); 4                                   5} 6} 这个tasks实在是简单吧,看看怎么部署上去,我们在xml文件中做如下定义: 1  2        jsTask 3        com.cj.ucdemo.JSTasks 4        session 5   这个配置文件也非常简单,那么看来所有的诀窍在与如何在JSP页面里面使用它。我们来看看:  1 添加一个task标签,把这个标签的mapId值指向地图控件”Map0”,讲它的值指向我们刚才定义的Managed Bean。访问一下,大家看看下面的浮动窗口结果: 是不是很令人惊奇啊?事实上我们这个Bean中只有一个方法,但是Tasks框架聪明地把这个方法名给提取出来,作为button的名字。你点击一下这个按钮,在后台就可以看到执行了这个方法,打印出了一条信息。简单的说:方法就是按钮!! 有了上个Task的基础, 我们来看其它的Task就驾轻就熟了。事实上,整个Task就是一个类,Task的Caption就是这个类的类名。里面的每一个Public方法都是Task浮动条上的一个按钮。那么参数怎么办呢?我们来加一个参数,并且加上一个它的get方法和set方法:  1String parameter; 2public String getParameter() { 3return parameter; 4} 5public void setParameter(String parameter) { 6this.parameter = parameter; 7}  看看我们的task工具条发生了什么变化,Task工具条如下图所示: 可以看到,多了一个Parameter的输入框,前面还有一个Label,很有意思吧。那个DoAnything是我加的另外一个方法。Task框架的扩展已经很明显了吧:把相同的功能集合在一个类里面,这个类可以接受参数。例如我们可以利用这个task进行buffer操作,这个输入框就可以用来输入buffer的距离。 看到这里,大家肯定想到了另外的几个问题,比如:怎么把这些参数,按钮的名字修改成中文,上次给人培训,有人说:用中文的变量名?也是一个办法,但是考虑的JDK对中文变量的处理,似乎有问题。还有其它的问题包括怎么跟地图进行交互?怎么样设置客户端的动作(画多边形还是画Polyline)。我们当然有另外的方法。 使用Taksinfo建立对Task的描述,所有的Taskinfo都需要继承自SimpleTaskInfo类。大家可以打开doc看看SimpleTaskInfo的子类有那些。事实上,那些子类都是已经存在的task的taskinfo,如果你需要做汉化,继承这些类并且改写其中的几个方法即可。 我们来看看我们自己的类需要改写SimpleTaskInfo的哪几个方法: TaskActionDescriptorModel[]getActionDescriptors(); 此方法用来修改command按钮的描述;command的意思是不需要和地图进行交互而是直接在服务器端执行一个命令。 TaskParamDescriptorModel[]getParamDescriptors() 此方法修改参数的描述; TaskToolDescriptorModel[]getToolDescriptors(); 此方法修改工具的描述,工具的意思是需要和地图进行交互; 那么我们来写一个类,直接说明如何使用这几个方法,修改task的表现: public TaskDescriptor getTaskDescriptor() { TaskDescriptor td=new TaskDescriptor(JSTasks.class); td.setDisplayName("我的任务"); return td; } 此方法修改了task的标题,将此类作为一个Managed-bean添加到faces-config.xml文件中,并且在.jsp页面中修改task的使用如下: 修改后的Task如下所示:  可以看到task的标题已经改变。下面我们来看修改Action的标题,代码如下: public TaskActionDescriptorModel[] getActionDescriptors() { TaskActionDescriptorModel[] td=new TaskActionDescriptorModel[1]; TaskActionDescriptorModel actionDesc=new TaskActionDescriptor(JSTasks.class,"doSomething","查询"); td[0]=actionDesc; return td; } 需要说明的是TaskActionDescriptor(JSTasks.class,"doSomething","查询")构造函数的三个参数,第一个是task类,第二个是方法名,第三个是修改后的方法名;构造后放到数组中返回即可,如果有多个方法,可以如法炮制,都放到数组中。修改后的task 有如下表现:   你会发现另外一个action没有了,别着急,因为我们还没有把它放到数组中,而原来默认的显示方式已经被我们修改了。 我们来修改doAnything工具,doAnything工具需要和地图交互,注意它的参数是MapEvent而不是TaskEvent,它完整的代码如下所示: public void doAnything(MapEvent te){ //通过和地图交互进行放大 WebContext ctx=te.getWebContext(); WebGeometry geom=te.getWebGeometry().toMapGeometry(ctx.getWebMap()); WebExtent ext = (WebExtent)geom; ctx.getWebMap().setCurrentExtent(ext); ctx.refresh(); } 如果跟地图交互,就要涉及到客户端执行的操作,我们这里没有任何地方指定客户端的操作,这是在taskinfo里面指定的,我们来看下面的代码: public TaskToolDescriptorModel[] getToolDescriptors() { TaskToolDescriptor [] toolDesAry=new TaskToolDescriptor[1]; TaskToolDescriptor toolDesc=new TaskToolDescriptor(JSTasks.class, "doAnything", "交互放大",ClientActions.MAP_RECTANGLE); toolDesAry[0]=toolDesc; return toolDesAry; } 注意TaskToolDescriptor构造函数的4个参数,第一个是task类,第二个是原方法名,第三个是替换后的方法名,第四个是客户端的动作。构造完成后放到数组中,如果有多个tools,如法炮制即可。修改后的task如下所示: 有了上面的解释,我们来看参数的taskinfo代码,就非常容易了: public TaskParamDescriptorModel[] getParamDescriptors() { // TODO Auto-generated method stub TaskParamDescriptorModel[] td=new TaskParamDescriptorModel[1]; TaskParamDescriptorModel paraDesc=new TaskParamDescriptor(SearchTask.class,"searchText","查询文本"); td[0]=paraDesc; return td; } 修改后的task如下所示: Task框架的基本构成就是这样了,祝大家使用愉快。下节将介绍TaskResult的使用。 7、Server Java 开发实战---自定义command 这个文档大家如果喜欢看英文,在ESRI的帮助里面就有。本文和ESRI的帮助文档类似,当然,我写的会加入自己的一些东西。 咱开发人员拿到一个开发平台,当然是要用来开发的,而且我们最好把它改得面目全非,才能显出开发人员我的水平所在。对于那些拿着模版生成一个web gis站点就去问客户要钱的事情,对于一个有自尊的开发人员,一般是不屑做的。 所以,我们今天就讲讲如何自定义命令和工具,先说说自定义命令吧。 命令事实上是command,也就是平常所在html页面里面的command,这些command里面就是命令按钮。在我们的ArcGIS Server的Web ADF里面,command被用来做一些不用跟地图交互的工作;比如缩放到全图:你不需要跟地图做任何交互,只要一按按钮,地图就缩放到全图。其它用处,就靠大家去发挥想象了,相信大家在以后的项目中会经常用到。我们以一个例子作为说明: package com.demo; import javax.faces.event.ActionEvent; import com.esri.adf.web.data.WebContext; import com.esri.adf.web.data.geometry.WebExtent; import com.esri.adf.web.faces.event.MapEvent; import com.esri.adf.web.faces.event.MapToolAction; public class MyFullExtent { WebContext context; public WebContext getContext() { return context; } public void setContext(WebContext context) { this.context = context; } public void setFullExtent(){ try{ WebExtent ex=context.getWebMap().getFullExtent(); context.getWebMap().setCurrentExtent(ex); context.refresh(); }catch(Exception ex){ ex.printStackTrace(); } } } 这是无比简单的一个command,简单到我都不好意思发出来给大家看了。注意这个类没有实现任何接口,也没有扩展任何现有的类,除了祖宗Object。它有一个WebContext,从上面一个讲座我们可以知道,这个类可是我们整个Web ADF的关键,有了它,可以得到所有的东西了。 这个类关键的方法是setFullExtent,里面的几行代码用来将地图缩放到全图,大家看看代码应该就可以明白。 怎么进行部署呢?就像普通的managed bean一样进行部署就可以了,打开jsf application的心脏faces-config.xml文件,在里面添加如下配置: myFullExtent com.demo. MyFullExtent session context #{mapContext} 注意我们把web context作为一个属性,用来初始化MyFullExtent,#{mapContext}指向了一个在faces-config.xml文件中定义的另外一个Bean,这个bean我们上次讲座已经详细解释过了。这里说一句废话,算是提醒一下,有请求过来的时候,先实例化这个类,然后再设置属性,有的工程师想在实例化里面进行一些操作,就必要用webContext,会报告空指针错误,因为这个时候webContext属性还没有被设置。 类写好了,也配置好了,怎么用呢,再给个例子: 自定义command就这样完成了。 8、 ArcGIS Server Java 讲座--ADF 体系结构 第一部分 这两天一直在等待9.2 Engine到货,好调试我写的例子,但是看来还得等一下,所以今天的这个讲座将向前跨越一大步。等Enging到货后把前面的部分,也就是如何自己写一个ADF补上。 有很多人看了前面的讲座问我,怎么讲了半天,还是没有讲到如何对server进行开发,就像吃浙大的豆沙包,咬了一大口,发现离馅还有300里。这句话提醒了我:那么今天我们直接吃豆沙馅了。 在讲一些sample之前,我们来看看整个ADF的体系架构,ADF里面有大部分类是纯JAVA的。它是构建在JSF之上的,所以它严格地遵守JSF的规范,所有的Bean都放在faces-config.xml和web.xml这样的配置文件中。我们今天吃豆沙馅,所以就讲讲这两个文件里面的配置; Faces-config.xml文件中有如下配置: esriWebSession com.esri.adf.web.data.WebSession session webApplication #{esriWebApplication} < /managed-bean> 这里定义了一个managed bean指向了WebSession的类,这个WebSession类实现了一个接口,这个接口是HttpSessionListener,看看这个接口的Javadoc吧;这个接口做两件事情:每当往Session中存入一个对象(setAttribute)或从Session中删除一个对象的时候,如果这个对象实现了此监听器接口,应用服务器将会自动调用接口相应的方法。你有机会在这两个方法里面做一些事情,最好的莫过于初始化存放在session中的变量。如果你一定要问我,Managed Bean的scope如果是session,那么在每个新session中,都会初始化这个managed bean,调用它的constructor,这样不是也可以实现managed bean地初始化么?如果你一定要这样写,也可以,但是系统的可扩展性很差啦。 在ADF中,如果我们写了一个类,并且希望这个类能够被ADF初始化,我们直接实现WebContextInitialize接口就可以了,ADF会自动初始化这个类,那么WebContextInitialize接口是什么呢? 首先,来看看我们的核心中的核心WebContext,Adf的广大Managed bean们都团结在WebContext周围,错了,是团结在WebContex里面;这个WebContext是这个Server ADF的容器。Context这个词翻译成上下文实在是不合适,我们还是叫它叫容器吧。 这个容器里面有什么?随便举个例子吧,它有很多的Attribute,比如有一个WebMap,所有容器里面的Attribute都需要实现一个接口,这个接口的名字叫做WebContextInitialize,这个伟大的接口有两个方法:init和destroy。看看这两个方法,我不说你都能想到它们是用来做什么的。初始化这个attribute,和销毁这个attribute。看个帮助里面的例子吧: WebContext context; public void init(WebContext context) { this.context = context; //初始化其它资源或者变量 } public void destroy() { context = null; //释放其它资源 } } OK,这个attribute也将作为一个managed bean部署在faces-config.xml文件里面。这里引出的问题是,谁来调用这个init方法和destroy方法呢?对JSF来说,这两个只是普通的方法,JSF不会调用这两个方法来进行资源的初始化和释放。答案是WebContext来调用这些初始化和释放的方法,我们可以看WebContext的doc原文: WebContext is responsible for making the callback methods implemented by the resources and attributes at appropriate junctures of the ADF application. The callback methods are declared in the WebContextInitialize, WebContextObserver and WebLifecycle interfaces. 原来是WebContext负责调用这三个接口的方法,初始化和释放资源。 我们看一下WebContext,发现它也是需要有人来初始化它的,那么WebContext是谁来负责的呢? 是WebSession。WebSession里面存放了所有的WebContext,并且保证这些WebContext们能够被正常的初始化和destroy,什么时候初始化,什么时候Destroy,当然由WebSession实现的,大名鼎鼎的HttpSessionBindingListener接口来实现,每次有一个用户访问或者用户退出时,该监听器会自动调用两个方法,用来初始化和摧毁WebContext。另外要说一句地时:一个session可以有多个webContext,你可以用getWebContexts()得到所有的WebContext。目前我们看到的所有的配置文件都只使用了一个WebContext,多个WebContext会使用在什么地方呢,这个问题大家先考虑一下,我在后面的讲座中会揭晓答案。 如果有多个用户访问时,就会产生多个并发用户,我们的Web Server会为每个用户都分配一个WebSession,这些Websession会放在哪里,答案是放在WebApplication中,我们打开faces-config.xml文件可以看到里面有一个webApplication的定义 esriWebApplication com.esri.adf.web.data.WebApplication 再去看WebSesstion的定义,有指向esriWebApplication的引用。 现在的问题是,是谁,初始化了WebApplication,我们祭出最原始也是最强大的武器了:ServletContextListener出马了,看web.xml中下面的顶定义, com.esri.adf.web.util.ADFServletContextListener ADFServletContextListener 实现ServletContextListener接口,ServletContextListener有两个方法:contextInitialized()和contextDestroyed(); 分别在web application启动的时候和结束的时候调用,你要是觉得好玩,可以写一个listener玩玩,我这里写一个简单的listener现现丑: public void contextInitialized(ServletContextEvent arg0) { System.out.println("context object inited."); } public void contextDestroyed(ServletContextEvent arg0) { System.out.println("context object destroyed."); } } ESRI使用了ADFServletContextListener,它实现了ServletContextListener接口,用来启动和destroy WebApplication。看看Javadoc的原文吧: The ADFServletContextListener initializes a WebApplication object and sets it as an attribute on the ServletContext. When the ServletContext is destroyed, it destroys the associated WebApplication and attempts to clean up all server hosted objects in the current thread. This class must be registered as a listener-class (in the web.xml file) for the web application. 现在大家都明白了吧,整个调用过程。Servelt容器启动的时候,会发送启动消息给ADFServletContextListener,ADFServletContextListener负责创建WebApplication,WebApplication负责创建和存放WebSession,WebSession负责创建WebContext,WebContext负责创建所有的Attribute,对资源的使用等等。这一下大家都满意了吧。 谨以此文,纪念在浙大吃过的一千个豆沙包。 9、ArcGIS Server JAVA讲座--实现后台JSF Bean和前台Javascript的联动 很多开发人员都梦寐以求的方案,我在论坛里面也看到很多人提出这样的问题,今天集中对这些问题做一个解答,也是作为Server JAVA讲座的一个部分。另外,要说明的是,今天的讲座不包括AJAX技术。AJAX技术会另开一个讲座。 先以一个问题开始,如果我们的webapplication中对geodatabase进行了编辑,大家都知道,这个时候需要一个提交编辑并保存,或者提交编辑并取消的过程,大家都用过ArcMAP,如果没有用过ArcMAP,word总是用过的,编辑过的东西,一关闭,肯定会给你警告。这个问题在桌面程序中简直就不是问题,在web上,我们倒是可以来说一说了,因为server和你的browser可能相距几万公里,你这边要关闭browser,在美国的服务器怎么能够知道你要关闭呢?服务器上运行的是JSF的Bean,而客户端运行的是Javascript,怎么让Javascript去感知后台的情况,给用户一个提示,这样就会很好地显示我们程序的友好性,对建设以人为本的和谐社会也大有好处。那么我们来看看如何做这个事情。 Browser关闭时会产生onUnload事件,有了这个事件,使我们有机会做一些事情,比如下面的语句,显示一个信息 在body中添加了这一句后,是不是已经看到了那个温柔的问句呢?如果是,那么你的Browser是支持这个onUnload事件的。下面来看这段代码: function checkTime(){ var isModified=; if(isModified ==true) alert("更新没有保存"); } 这个代码很有意思,其它的好说,不好理解,这是JSTL,如果没有见过JSTL,google或者baidu一下看看吧。editBean是一个标准的JSF的bean,它在faces-config.xml文件中部署,它有一个属性和属性的getter,setter方法,如下: private boolean edited=false; public boolean isEdited() { return edited; } public void setEdited(boolean edited) { this.edited = edited; } 你在开始编辑的时候,设置edited为true就可以了。JSTL能够用过标签访问到这个bean的值。这样,就实现了前台Javascript和后台JSF bean的联动。 是不是看了这个文章,马上摩拳擦掌,准备去修改您的程序呢? 10、ArcGIS Server JAVA讲座 --AJAX基础 咱也来跟跟潮流,讲一讲AJAX和JSF如何结合。我相信,这也是将来SERVER JAVA的开发人员都会问的一个问题。我们来看看如何能够让AJAX能够在服务器上和客户端跑起来。 要应用AJAX的条件是: 1、 一个支持javascript的浏览器,当然,现在主流的浏览器都是支持Javascript的。 2、 浏览器必须支持XMLHTTP或者XMLHttpRequest对象。 3、 能够以XML发送响应的服务器端。服务器端可以有很多的技术来实现,不管你是用ASP,JAVA,都可以实现。当然,本文要讨论的局限于JSF技术。 对于熟悉大多数服务器端程序编写的工程师,比如我,Javascript完全是另外一个世界。但是了解Javascript,将使你的程序更加强大,开发起来更加容易。一个很有意思的功能是如果用户在编辑地图的时候,编辑到中途,忘记保存了,这提醒我们可以做一个比较有意思的功能,自动保存。在google mail中,你是不是已经感觉到过这个自动保存的功能了呢?反正我当时看到的时候吓了一跳。以一个服务器端程序员的思路去思考,会觉得不可思议。事实上这个功能就可以用Ajax技术来实现,用一个定时器,每隔5分钟自动发送一个保存的请求就可以实现这样的功能。 再看一个自动查找的例子,比如一个输入一个用户后自动出来和该用户有关的信息,可以通过这样的方法来实现: Search(‘query’)方法可以发送一个Ajax请求到客户端,进行查询后,将结果以XML请求返回,这样就不需要刷新整个页面而进行部分更新。就这么一个小小的技术,让用户在继续Web 浏览的时候感觉更好,也成为了各个网站争相模仿的对象,一些开发人员也发现了宝贝一样言必称Ajax,甚至成为项目选型的关键,这大概也是最初的开发者想象不到的。 来看看Javascript如何来发送一个XMLHttpRequest请求的。来看一下下面的代码: if (window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if (window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } 这是再简单不过的实例化XMLHttpRequest的一段代码,实例化一个之后,你就可以像操作一个普通的对象一样操作它,它有很多的方法,比如下面的方法是建立和服务器端的链接: req.open("GET", url, true); 第一个参数是HTTP请求的方式:”Get”或者”Post”,第二个参数是服务器的URL,第三个参数是是否进行非同步的请求。大家如果已经仔细研究了我们ESRI的工具标签,你会发现这些标签里面有一个clientPostBack属性可以设置是否进行异步请求。我们可以知道,Web ADF在后面肯定用了open方法的第三个参数来进行。要小心的是,如果你要设置clientPostBack为true,你的javascript代码可能需要重新构建一下。不然这些代码可能不会被执行。因为异步请求更新的是部分页面。 异步请求的意思是在请求时浏览器可以做其它的事情,那么如何得知服务器端的处理已经结束,可以更新页面上的数据了呢?我们来看看XMLHttpRequest对象的方法 req.onreadystatechange = processXMLResponse; 当服务器端的处理结束时,就可以调用processXMLResponse方法,我们就可以通过对这个方法进行编程来处理请求,当然可以是任何的名字的方法,只要将方法注册到onreadystatechange方法即可。 XMLHttpRequest初始化完成后就可以发送到请求到服务器端了,用一个很简单的方法即可: req.send(null); 当然,一般get用的实在太少,我们最关心的是Post,看看Post应该怎么做: req.open("POST", "/agsviewer", true); req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"; req.send("__ADFPostBack__=true&formId=f"+"&mapId=Map0&ajaxdemo=ajaxdemo "); Post可以发送一些数据到服务器端,这正是我们所需要的。我们来继续看看一个请求当前比例尺的代码: function updateInfoResponse(xmlHttp) { if (xmlHttp != null && xmlHttp.readyState == 4 && xmlHttp.status == 200) { var xml = xmlHttp.responseXML; document.getElementById("scale").value = "1:" + xml.getElementsByTagName("scale").item(0).firstChild.nodeValue; } } 看一下判断了(xmlHttp != null && xmlHttp.readyState == 4 && xmlHttp.status == 200,判断4是表示处理已经结束,判断200表示HTTP请求正常,相信大家都见过404的HTTP请求,令人讨厌的“page not found”。 判断结束后,就用xml.getElementsByTagName("scale").item(0).firstChild.nodeValue;语句把比例尺取过来。 相信大家都大致知道AJAX是怎么回事了,下一次我会写一个简单的例子。 IBM网站上有一篇很好的介绍AJAX的文章,大家可以去看看: http://www-128.ibm.com/developerworks/cn/xml/wa-ajaxintro1.html 11、ArcGIS Server JAVA开发讲座--- ADF中的Ajax函数 ESRI为了使Server ADF能够支持Ajax,做了大量的工作,Sever ADF中有很多可以利用的代码,我们来看看: 代码1:创建XMLHttpRequest,可以看一下下面的代码: this.createXmlHttpObject = function() { if (this.isIE) { try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (exception) { return new ActiveXObject("Microsoft.XMLHTTP"); } } return new XMLHttpRequest(); } 这个代码写得很妙吧,前面两个处理IE浏览器,最后一个支持Firefox,XMLHttpRequest是Firefox的。 发送请求,用如下命令: EsriUtils.sendAjaxRequest(url, params, true, function() { updateInfoResponse(xmlHttp); }); 解释一下,怎么得到这个url,ESRI也提供了办法: var url = EsriUtils.getServerUrl(formId); getServerUrl的代码如下: this.getServerUrl = function(fId) { if (document.forms[fId].action.indexOf(";jsessionid=") != -1) return document.forms[fId].action; else if (this.getJSessionId()) return document.forms[fId].action + ";jsessionid=" + this.getJSessionId(); else return document.forms[fId].action; } 嗯,url已经解释了,我们看看第二个参数params,这个参数包含了我们需要传递到服务器端的参数。看一下源代码就可以知道,xh.send(params);将这些参数发送到服务器端。 doGet参数就比较简单了,用来说明这个请求是get还是post。看看源代码,发送的方法是不一样的,如果是get方法,那些参数用?进行连接,传送到服务器端。它返回的仍然是XMLHttpRequest对象。 Callback函数是我们自己写的函数,用来在服务器数据处理完成后,处理服务器端传送过来的数据。大家看着是不是一目了然了呢? sendAjaxRequest的源代码 this.sendAjaxRequest = function(url, params, doGet, callback) { try { var xh = this.createXmlHttpObject(); xh.onreadystatechange = callback; if (doGet) { xh.open("GET", url + "?" + params, true); xh.send(null); } else { xh.open("POST", url, true); xh.send(params); } return xh; } catch (exception) { return null; } } } 有了这几个强大的函数,我们就可以利用ESRI的成果,来进行Ajax的开发了。说了半天客户端的东西,下一篇我们来讲服务器端的处理,服务器端的处理需要比较大的篇幅来说。 12、ArcGIS Server Java讲座--如何在用JSF在服务器端处理Ajax请求 服务器端的处理才是整个事件的Key,那么我们来看看整个请求,回复的过程,我们通过下面的图看看我们服务器端的代码怎么样才可以plug in进去: 看了这个图您肯定已经明白了,我们有机会得到客户端发送过来的HTTP请求,得到这个请求,得到请求里面的参数,我们就可以捋起袖子,大干一番了。 我们用什么方式去处理呢,最简单的一个方法是自己写一个servlet,在servlet的Dopost方法里面,写上处理这个请求的代码,并且把结果以XML的方式返回回去,那么我们这里用一个简单的例子:      throws java.io.IOException {   String action = req.getParameter("action");   String item = req.getParameter("item");      if ((action != null)&&(item != null)) {     // Add or remove items from the Cart     if ("add".equals(action)) {       cart.addItem(item);     } else if ("remove".equals(action)) {       cart.removeItems(item);     }   }   // Serialize the Cart's state to XML   String cartXml = cart.toXml();   // Write XML to response.   res.setContentType("application/xml");   res.getWriter().write(cartXml); } 这对于了解服务器端编程的人来说,是最简单不过的了。当然,我们用了JSF框架,肯定不会再通过Servlet技术去做这件事情。我们这里用JSF的PhaseListener进行处理。什么是PhaseListener,我们来看一下PhaseListener的JavaDoc: extends java.util.EventListener, java.io.Serializable An interface implemented by objects that wish to be notified at the beginning and ending of processing for each standard phase of the request processing lifecycle. 原来,这个接口捕捉到每个Request的声明周期,在请求开始处理和请求处理结束时我们可以插入我们自己的代码。这个接口的两个最重要的方法如下: Handle a notification that the processing for a particular phase has just been completed.  void beforePhase(javax.faces.event.PhaseEvent event)           Handle a notification that the processing for a particular phase of the request processing lifecycle is about to begin.   处理结束和处理开始两个方法,我们可以在这两个方法里面写入我们自己的代码,对用户过来的请求进行处理。    比如,我们可以在afterPhase方法里面,插入下面的代码对数据进行处理:     ExternalContext externalContext = facesContext.getExternalContext(); Map paramMap = externalContext.getRequestParameterMap(); 通过paramMap里面可以得到所有的参数,当然,我们并不是对每一个请求都会进行处理,所以,在客户端Javascript发送Ajax请求得时候,可以添加一个自定义的参数,比如我们前面文档中的ajaxdemo=ajaxdemo参数,这样我们可以判断一下当前是不是我们要处理的请求: if (! AJAX_DEMO.equals(paramMap.get(AJAX_DEMO)))       return;    接下去的代码,就可以处理我们的各种各样的请求了,我们在这里要举的例子是如何得到当前地图的比例尺。下次讲座再深入探讨吧。 13、ArcGIS Server Java讲座----利用Ajax得到当前地图的比例尺(服务器端) 现在我们进入实战阶段,看看我们怎么可以在服务器端得到当前地图的范围,这个代码中你将学到如何从faces Context里面得到当前form中的控件,以及如何利用ESRI提高的工具,简化我们响应AJAX请求的过程: UIComponent form = facesContext.getViewRoot().findComponent((String) paramMap.get(FORMID)); 首先我们得到页面上的form,form是一个container,通过它我们可以得到form中的其它组件,比如我们的地图控件: 我们可以用下面的方法得到这个Mapcontrol和WebMap: MapControl mc = (MapControl) form.findComponent((String) paramMap.get(MAPID)); WebMap wm = mc.getWebMap(); 得到了这个WebMap,哈哈,可以得到Everything了,因为你可以从WebMap中得到WebContext。这个时候,想得到那个比例尺,是再简单也不过的事情了,用下面的代码即可: wm.getMapScale(); 得到了比例尺了,接下去怎么办呢?把它返回到客户端,我们的Ajax客户端认的是XML的流,咋办呢?最笨的办法当然是是一个一个String的组装成XML,然后调用Response的Write写回到客户端,比如下面的代码: public String toXml() { StringBuffer xml = new StringBuffer(); xml.append("\n"); xml.append(“”); xml.append(“100”); xml.append("\n"); return xml.toString(); } 生成了XML代码,然后用下面的代码把结果返回给客户端: Response res=(Response)externalContext.getResponse(); res.setContentType("application/xml"); res.getWriter().write(scaleXml); 当然,各位看官想来都是JAVA高手,对于这样的写法实在感觉不舒服,正待想办法用标准的XML工具来写时,你会发现,ESRI已经为您想到了,写好了几个工具类来供你调用了。我们来看看怎么用这些工具: 用下面的代码创建一个标准的XML文档: Document doc = XMLUtil.newDocument(); 用下面的代码新建一个element: Element responseElement = XMLUtil.createElement(doc, "response", null, null); 用下面的代码将我们的比例尺数据写到刚才新建的Element里面: XMLUtil.createElement("scale", String.valueOf(wm.getMapScale()), responseElement); 然后用下面的语句将XML文档写入到客户端: AJAXUtil.writeResponse(facesContext, doc); 很可惜的是,ESRI这些类的代码是不公开的,不过你可以猜一猜,这些方法里面到底发生了什么事情。 OK,皮球又被提到了客户端了,我们下一个讲座来看看,如何解析服务器端返回的XML文档,并且更新页面上的输入框。 14、ArcGIS Server Java讲座--用Ajax得到地图比例尺(客户端代码) 上个讲座我们讲到,服务器端已经得到了当前地图的比例尺,我们需要在客户端做一个处理,把这个比例尺数据给用户看。我们在页面上放一个文本框,用来显示比例尺数据,如下面的代码所示: Scale 我们如何将服务器传过来的数据更新这个文本框呢?来看看前面讲座中提到的往服务器端发送请求时候的那个回调函数: var xmlHttp = EsriUtils.sendAjaxRequest(url, params, true, function() { updateInfoResponse(xmlHttp); }); 我们把回调函数的代码也贴在这里: function updateInfoResponse(xmlHttp) { if (xmlHttp != null && xmlHttp.readyState == 4 && xmlHttp.status == 200) { var xml = xmlHttp.responseXML; var scale=xml.getElementsByTagName("scale").item(0).firstChild.nodeValue; document.getElementById("scale").value = "1:" + scale } } 这个代码相比大家都能看明白了,先判断服务器端是不是已经处理结束,并且服务器的状态一切正常,然后得到服务器返回的xml,通过getElementsByTagName把里面包含着的比例尺数据提取出来,组合中我们熟悉的文本,然后把这个文本填充到ID为scale的文本框中。 我们来总结一下,我们发送了一个请求,这个请求包含了MapID参数,和一个标志参数ajaxdemo,MapID参数可以让服务器得到MapControl的实例和WebMap,进而得到比例尺。Ajaxdemo让服务器端的处理程序可以正确的辨识这个请求是我们这个PhaseListener需要处理的请求,处理结束后,返回XML到客户端。客户端的回调函数处理XML文档,提取出比例尺信息,写入到页面的文本框中。撩起Ajax神秘的面纱后,发现原来Ajax不过如此,老酒装新瓶,重新包装了一下。 讲到这里,大家心中想必还有两个疑点,第一个是说了半天,我们是怎么触发这个请求的呢?通过定时器触发么?不是。我们是通过地图的变化进行触发的,这样是最最合理的,每当地图更新了,客户端会自动发送一个请求,要求地图变化后的比例尺。非常合理,但是怎么进行设置呢?我们经常说,我们的控件是支持Ajax的,如何支持?我们支持通过监听器的方式,监听地图的变化,如下面的代码所示: var map = EsriControls.maps["Map0"]; map.addUpdateListener("request", updateInfoRequest); 这样,每次地图更新时,都会触发updateInfoRequest函数,该函数用来向服务器端发送请求。 另外一个疑问是Phaselistener怎么设置,看一下JavaDoc可以知道,我们可以在faces-config.xml文件中,用下面的语句进行设置: demo.AjaxDemoPhaseListener 恩,已经完美了。 Ajax的讲座到此告一段落了,欢迎大家讨论。 15、ArcGIS Server JAVA 讲座 自定义Functionality 今天我们来讲讲如何自定义Functionality,自定义Functionality有很多用处。它最大的用处是可以访问数据源。我们来看看已经存在的Functionality: AGSMapFunctionality, AIMSMapFunctionality, AWSMapFunctionality, EJBMapFunctionality, WMSMapFunctionality等等等等,查看GISFunctionality接口的子类你可以看到ADF中更多的Functionality。 这些Functionality无一不跟后台的资源有关。我们来看看Functionality接口的定义: public void initFunctionality(GISResource arg0) { } public void destroyFunctionality() { } public GISResource getResource() { return null; } 每个functionality都需要实现Functionality这个接口,这个接口里面有三个方法,初始化方法,destroy方法和得到资源的方法。GISResource对应于一个特定的资源,比如我们最常用的资源就是AGSLocalMapResource,当然也可以是ArcIMS的Resource,取决于你把这个functionality注册到哪个Resource中。我们来看一个例子,虽然Functionality一般跟资源有关,但是也可以做其它的事情,比如我们这里举一个非常简单的例子,FixedZoomin: public class DemoFunction implements GISFunctionality { WebContext ctx; public void zoomInFunction(){ WebMap wmap = ctx.getWebMap(); WebExtent ext = wmap.getCurrentExtent(); ext.expand(0.5); wmap.setCurrentExtent(ext); ctx.refresh(); } public void initFunctionality(GISResource arg0) { ctx=arg0.getWebContext(); System.out.print("Demo Function inited"); } public void destroyFunctionality() { System.out.print("Demo Function destroyed"); } public GISResource getResource() { return null; } } 非常简单的一个例子,从GISResource里面得到WebContext,然后得到WebMap,修改WebMap的Extent,放大功能就实现了。你说非得跟资源有关系,那么确实也有点关系,因为我们还是使用了后台的数据来进行操作。需要说明的是,我们这个代码对于所有的Resource都是适用的,不管你后台使用了ArcIMS,还是其它的Resource。ADF的妙处也在这里。 写好了这个Funcionality,那么我们来看看如何部署的,每一个Functionality都需要部署到某个Resource中,那么我来看一下,这个functionality如何来部署: ags1 com.esri.adf.web.ags.data.AGSLocalMapResource none functionalities [AGSWS_MAPSERVER_ENDPOINT] 4、好了,最后修改一个地方,在WebContext中注册的两个resources,其中一个是我们熟悉的ags0,另一个是agswsExt1,分别是地图服务和NA扩展。细心的人也许已经发现了,它们都注册了自己的functionalities,但是用的是两套变量。ags0用的是 #{agsMap} 而agswsExt1用的是 map #{agswsMap} 看到区别了吧,其实它们指向的是同一个变量,这个变量是在WEB-INF/functionalities/ags-functionalities.xml中定义的: com.esri.adf.web.ags.data.AGSMapFunctionality none 看到了吧,好大一个坑,被我发现了javascript:window.open(this.src); alt=图片点击可在新窗口打开查看 src="/article/UploadPic/2007-6/2007626142951155.gif" onload=imgresize(this); border=0 smilieid="9">。原来ags0引用错了,要把它改成与ags-functionalities.xml中定义的一样。当然,您也可以不修改,先运行看看会有什么样的Exception出现,很有意思。 selectedImage="images/tasks/routing/addstopD.gif" clientAction="EsriMapPoint" serverAction="com.wj.adf.web.route.AddOriginPoint" clientPostBack="true" toolTip="Add start point"/> 看这段代码大家应该就知道这个tool执行什么任务了javascript:window.open(this.src); alt=图片点击可在新窗口打开查看 src="/article/UploadPic/2007-6/2007626142951237.gif" onload=imgresize(this); border=0 smilieid="1"> 不错,目的就是让用户选择这个工具在地图上进行选点,执行了EsriMapPoint行为。服务器端代码需要执行两个操作,一是保存这个点的坐标(它是一个WebPoint类型,下面就称为originPoint);二是在地图上用自定义的符号绘制这个点,why?因为默认情况下EsriMapPoint这个行为不会在地图上做任何标记,那用户怎么能知道起点是否选中了呢?!没关系,看我的。 先定义一个标记的样式: //定义标记的样式 originMarker = new WebTrueTypeMarkerSymbol(); originMarker.setAntialiasing(true); originMarker.setMarkerColor("72,145,91"); originMarker.setOutlineColor("5,5,5"); originMarker.setMarkerType(WebTrueTypeMarkerSymbol.CIRCLE); originMarker.setGlowingColor("125,125,125"); 下面揭开com.wj.adf.web.route.AddOriginPoint这个类的神秘面纱 private WebTrueTypeMarkerSymbol originMarker; protected GraphicElement stopsElement; public AddOriginPoint(){ //实例化originMarker和stopsElement ...... } public void execute(MapEvent event) throws Exception { WebContext ctx = event.getWebContext(); WebPoint point = (WebPoint)event.getWebGeometry(); //将屏幕坐标转换为地图坐标 point = WebPoint.toMapPoint(point, ctx.getWebMap().getCurrentExtent(), ctx.getWebMap().getWidth(), ctx.getWebMap().getHeight()); WebGraphics graphics = ctx.getWebGraphics(); if(graphics == null){ System.out.println("WebGraphics is null"); return; } //将这个点显示在地图上 stopsElement.setGeometry(point); //给该点一个编号,因为是起点,所以编号始终为1 ArrayList pList = new ArrayList(); pList.add("1"); originMarker.setTextValues(pList); stopsElement.setSymbol(originMarker); graphics.addGraphics(stopsElement); //保存point,保存的策略大家可以自由选择,我是将point都存到了WebSession中 ...... } } 哈哈,这下大家看到WebGraphics的妙处了吧,贴个效果图 javascript:window.open(this.src); alt=图片点击可在新窗口打开查看 src="/article/UploadPic/2007-6/2007626142951817.gif" onload=imgresize(this); border=0>图片附件: [图标1就是用WebGraphics画的] 44.JPG (2006-12-20 03:57 PM, 14.15 K) javascript:window.open(this.src); alt=图片点击可在新窗口打开查看 src="/article/UploadPic/2007-6/2007626142951712.jpg" onload=imgresize(this); border=0> 在取得networkAnalystFunc之后,随便写个类如RouteFind,注册faces-config.xml, RouteFind里面有这个方法getSearched,放到toolbar里面, public void getSearched(MapEvent event,boolean click){ if(!doubleClick){ pointStrart= (WebPoint) event.getWebGeometry().toMapGeometry(webMap); networkAnalystFunc.setStopSymbol(originMarker); networkAnalystFunc.addStop(pointStrart); }else{ pointEnd = (WebPoint) event.getWebGeometry().toMapGeometry(webMap); networkAnalystFunc.clearRoute(); networkAnalystFunc.setRouteSymbol(routeLine); networkAnalystFunc.setStopSymbol(destinationMarker); networkAnalystFunc.addStop(pointEnd); results = networkAnalystFunc.solve(); } } 21、ArcGIS Server开发——控制图层是否可见   当需要控制图层的可见性时,可以使用 IMapDescription mapdescription = webMap.MapDescription; webMap.ManageLifetime(mapdescription);      ILayerDescriptions layerdec = mapdescription.LayerDescriptions;     for(int i=0;i < mapdescription.LayerDescriptions.Count; i++) {     ILayerDescription onelayerdesc = layerdec.get_Element(i);      onelayerdesc.Visible = true; }   22、航线查询问题-Server Java的实现方法 数据准备 1、航线的相关数据,google了一部分南航的数据,只要有几十条线路做演示就足够用了。将它们保存在数据库表中,主要有出发城市和到达城市两个字段。 2、用作地图显示的行政区划图以及城市的点要素图。为了简化,只选择了涉及上述航线的若干城市。城市数据是一个point的shapefile文件,其中有一个属性列为城市名称“name”,为了方便检索,name属性中保存的城市名称与数据库中航线信息所保存的城市名称是匹配的。 实现方法 ADF提供了很好用的task framework,因此这个例子以自定义task的形式实现。先来简单了解一下整个操作的流程: 用户选择了自定义工具栏提供的一个工具,与地图进行一个点查询的交互,选择一个起点城市。在服务器端根据用户的操作将得到一个点的坐标,根据这个坐标进行查询,得到这个坐标点所表示的点要素的相关信息,这里我们所关心的只是这个城市的名称。知道了城市名称以后,以它作为起点城市,到数据库中检索所有从该城市出发的航线的目的地城市。这是一个字符串类型的数组,接下来可以根据这些城市名到地图中查找它们对应的点要素,并获得这些点要素的坐标。这样,我们就有了一个起点和若干个终点的坐标,可以绘制航线了。好了,基本思路就是这样,来看看怎么实现 先创建一个自定义task类,它有一个方法: public class SearchAirlinesTask{ public void getAirlines(MapEvent event){ } } 声明了这个类以后,task framework就会根据getAirlines(MapEvent event)方法的参数来判断这个工具的类型。由于MapEvent涉及的地图交互操作有很多种,而这里我们需要的是点查询操作,所以接下来我们需要再构造一个类来给自定义task加一些说明: public class SearchAirlinesTaskInfo extends SimpleTaskInfo{ private TaskToolDescriptor[] taskTool = new TaskToolDescriptor[1]; public SearchAirlinesTaskInfo(){ taskTool[0] = new TaskToolDescriptor(SearchAirlines.class,"getAirlines","选择起点",ClientActions.MAP_POINT); taskTool[0].setToolTip(“从地图上选择一个起点”); } public TaskToolDescriptorModel[] getToolDescriptors(){ return taskTool; } } 然后将这个taskInfo类添加到我们的自定义task中: public class SearchAirlinesTask{ private SearchAirlinesTaskInfo taskInfo = new SearchAirlinesTaskInfo(); public void getAirlines(MapEvent event){ } public SimpleTaskInfo getTaskInfo(){ return taskInfo; } } 完成了上面的准备步骤以后,我们深入到getAirlines(MapEvent event)方法的内部,看看它是怎么运行的: public getAirlines(MapEvent event){ WebContext ctx = event.getWebContext(); WebGraphics graphics = ctx.getWebGraphics(); WebQuery query = ctx.getWebQuery(); } 所有的查询操作都是通过WebQuery来进行的,查询的结果将通过WebGraphics绘制到地图上。查询的时候需要提供两个信息,点的坐标以及目标图层: WebPoint point = (WebPoint)event.getWebGeometry().toMapGeometry(ctx.getWebMap()); IdentifyCriteria ic = new IdentifyCriteria(point); ic.setMaxRecordCount(1); //只需要查询city图层,该图层保存的是城市的相关信息 List layers = query.getQueryLayers(); List queryLayer = new ArrayList(); for(Iterator iter=layers.iterator(); iter.hasNext();){ Object item = iter.next(); if(item instanceof WebLayerInfo){ WebLayerInfo layerinfo = (WebLayerInfo)item; if(layerinfo.getName().equals(“city”)){ queryLayer.add(layerinfo); } } } List rs = query.query(ic, queryLayer); ok,用户与地图交互操作的查询就完成了,接下来我们需要从查询结果中得到这个城市的城市名。 if(rs.size() > 0){ Iterator iter = rs.iterator(); QueryResult item = (QueryResult)iter.next(); Object obj = item.getDetails().get(“name”); String startCity = obj.toString(); WebPoint start = (WebPoint)item.getHighlightGeometry(); } 这里需要解释一下的是最后一句,也许有人会问,刚才我们不是已经获取了一个point坐标了吗,那个不就是起点城市么?事实上,我们在地图上点击查询的时候不会恰好就点在表示城市的那个点的中心,WebQuery查询的时候是有一个距离容错的。而绘制航线的时候要求更精确,所以我们以查询得到的点要素的坐标为准。 费了半天劲总算把城市名给找出来了,擦一把汗,接着查,我们还需要终点城市的坐标呢!刚才我们用的是WebGeometry来查询,接下来我们将使用文本进行查询。 DbAirlineSearch dboper = new DbAirlineSearch(); String[] destinations = dboper.getDestinations(startCity); //终点城市的坐标 WebPoint[] ends = new WebPoint[destinations.length]; //航线 WebPath[] airlines = new WebPath[destinations.length]; //在WebGraphics中绘制航线所需的要素 GraphicElement[] linesElements = new GraphicElement[destinations.length]; for(int i=0;i searchFields = new ArrayList(); searchFields.add(“name”); tc.setSearchFields(searchFields); tc.setSearchText(destinations[i]); List rs2 = query.query(tc,queryLayer); //处理文本查询的结果 if(rs2.size() > 0){ QueryResult destination = (QueryResult)rs2.iterator.next(); ends[i] = (WebPoint)destination.getHighlightGeometry(); //得到一个终点坐标以后就可以绘制一条航线了 List pointList = new ArrayList(); pointList.add(start); pointList.add(ends[i]); airlines[i] = new WebPath(pointList); WebPolyline polyline = new WebPolyline(); polyline.addPath(airlines[i]); linesElement[i] = new GraphicElement(); linesElement[i].setGeometry(polyline);//这里需要说明一下,如果这里用的是WebPath,将不会绘制出航线,换成WebPolyline以后就可以了。 linesElement[i].setSymbol(lineSymbol); graphics.addGraphics(linesElement[i]); } } 到这里,航线的绘制功能就算完成了。后续的文章中将介绍如何将航线的其他信息添加到WebResults中,以及高亮显示某一条航线的功能 23、ArcGis 航线查询完全例子 主要功能 1、可以在地图上点击一个起点城市,自动画出从该城市出发的所有航线;也可以通过文本框输入起点城市进行查询,然后绘制航线; 2、将查询结果显示给用户。当用户选择一条具体的航线时,列出该航线的相关信息,包括起点城市、终点城市、出发时间、到达时间、票价等; 3、根据需要,用户可以高亮显示某条航线,另外还有ZoomTo等功能。 数据准备 1、航线的相关数据,google了一部分南航的数据,虽然少,但是做demo也差不多了。其中票价、起飞时间和到达时间是为了演示需要添加的三个字段。 出发城市 到达城市 票价 起飞时间 到达时间 1 | 海口 | 北京 | 1201.00 | 8:35 | 14:35 2 | 海口 | 广州 | 1202.00 | 8:35 | 14:35 3 | 海口 | 深圳 | 1203.00 | 8:35 | 14:35 4 | 海口 | 南京 | 1204.00 | 8:35 | 14:35 5 | 海口 | 贵阳 | 1205.00 | 8:35 | 14:35 6 | 海口 | 成都 | 1206.00 | 8:35 | 14:35 7 | 海口 | 上海 | 1207.00 | 8:35 | 14:35 8 | 海口 | 郑州 | 1208.00 | 8:35 | 14:35 9 | 海口 | 长沙 | 1209.00 | 8:35 | 14:35 10 | 海口 | 西安 | 1210.00 | 8:35 | 14:35 11 | 海口 | 昆明 | 1211.00 | 8:35 | 14:35 12 | 海口 | 温州 | 1212.00 | 8:35 | 14:35 13 | 海口 | 大连 | 1213.00 | 8:35 | 14:35 14 | 海口 | 武汉 | 1214.00 | 8:35 | 14:35 15 | 海口 | 沈阳 | 1215.00 | 8:35 | 14:35 16 | 海口 | 哈尔滨 | 1216.00 | 8:35 | 14:35 17 | 海口 | 重庆 | 1217.00 | 8:35 | 14:35 18 | 海口 | 南昌 | 1218.00 | 8:35 | 14:35 19 | 海口 | 乌鲁木齐 | 1219.00 | 8:35 | 14:35 20 | 北京 | 重庆 | 1220.00 | 8:35 | 14:35 21 | 北京 | 成都 | 1221.00 | 8:35 | 14:35 22 | 北京 | 南昌 | 1222.00 | 8:35 | 14:35 23 | 北京 | 长沙 | 1223.00 | 8:35 | 14:35 24 | 北京 | 乌鲁木齐 | 1224.00 | 8:35 | 14:35 25 | 北京 | 三亚 | 1225.00 | 8:35 | 14:35 26 | 北京 | 延吉 | 1226.00 | 8:35 | 14:35 27 | 北京 | 昆明 | 1227.00 | 8:35 | 14:35 28 | 西安 | 榆林 | 1228.00 | 8:35 | 14:35 29 | 西安 | 银川 | 1229.00 | 8:35 | 14:35 30 | 西安 | 南京 | 1230.00 | 8:35 | 14:35 31 | 西安 | 长沙 | 1231.00 | 8:35 | 14:35 32 | 西安 | 海口 | 1232.00 | 8:35 | 14:35 33 | 西安 | 深圳 | 1233.00 | 8:35 | 14:35 34 | 西安 | 广州 | 1234.00 | 8:35 | 14:35 35 | 深圳 | 成都 | 1235.00 | 8:35 | 14:35 36 | 深圳 | 重庆 | 1236.00 | 8:35 | 14:35 37 | 深圳 | 武汉 | 1237.00 | 8:35 | 14:35 38 | 深圳 | 宜昌 | 1238.00 | 8:35 | 14:35 39 | 深圳 | 襄樊 | 1239.00 | 8:35 | 14:35 40 | 温州 | 南京 | 1240.00 | 8:35 | 14:35 41 | 温州 | 烟台 | 1241.00 | 8:35 | 14:35 42 | 温州 | 厦门 | 1242.00 | 8:35 | 14:35 43 | 温州 | 赣州 | 1243.00 | 8:35 | 14:35 把这些数据导入到数据库的airlines表中。 2、用作地图显示的行政区划图以及城市的点要素图。为了简化,只选择了涉及上述航线的若干城市。城市数据是一个point的shapefile文件,其中有一个属性列为城市名称“name”,为了方便检索,name属性中保存的城市名称与数据库中航线信息所保存的城市名称是匹配的。 实现方法 ADF提供了很好用的task framework,因此这个例子以自定义task的形式实现。先来简单了解一下整个操作的流程: 用户选择了自定义工具栏提供的一个工具,与地图进行一个点查询的交互,选择一个起点城市。在服务器端根据用户的操作将得到一个点的坐标,根据这个坐标进行查询,得到这个坐标点所表示的点要素的相关信息,这里我们所关心的只是这个城市的名称。知道了城市名称以后,以它作为起点城市,到数据库中检索所有从该城市出发的航线的目的地城市。这是一个字符串类型的数组,接下来可以根据这些城市名到地图中查找它们对应的点要素,并获得这些点要素的坐标。这样,我们就有了一个起点和若干个终点的坐标,可以绘制航线了。好了,基本思路就是这样,来看看怎么实现J 先创建一个自定义task类,它有一个方法: public class SearchAirlinesTask{ public void getAirlines(MapEvent event){ } } 声明了这个类以后,task framework就会根据getAirlines(MapEvent event)方法的参数来判断这个工具的类型。由于MapEvent涉及的地图交互操作有很多种,而这里我们需要的是点查询操作,所以接下来我们需要再构造一个类来给自定义task加一些说明: public class SearchAirlinesTaskInfo extends SimpleTaskInfo{ private TaskToolDescriptor[] taskTool = new TaskToolDescriptor[1]; public SearchAirlinesTaskInfo(){ taskTool[0] = new TaskToolDescriptor(SearchAirlines.class,"getAirlines","选择起点",ClientActions.MAP_POINT); taskTool[0].setToolTip(“从地图上选择一个起点”); } public TaskToolDescriptorModel[] getToolDescriptors(){ return taskTool; } } 然后将这个taskInfo类添加到我们的自定义task中: public class SearchAirlinesTask{ private SearchAirlinesTaskInfo taskInfo = new SearchAirlinesTaskInfo(); public void getAirlines(MapEvent event){ } public SimpleTaskInfo getTaskInfo(){ return taskInfo; } } 完成了上面的准备步骤以后,我们深入到getAirlines(MapEvent event)方法的内部,看看它是怎么运行的: public getAirlines(MapEvent event){ WebContext ctx = event.getWebContext(); WebGraphics graphics = ctx.getWebGraphics(); WebQuery query = ctx.getWebQuery(); } 所有的查询操作都是通过WebQuery来进行的,查询的结果将通过WebGraphics绘制到地图上。查询的时候需要提供两个信息,点的坐标以及目标图层: WebPoint point = (WebPoint)event.getWebGeometry().toMapGeometry(ctx.getWebMap()); IdentifyCriteria ic = new IdentifyCriteria(point); ic.setMaxRecordCount(1); //只需要查询city图层,该图层保存的是城市的相关信息 List layers = query.getQueryLayers(); List queryLayer = new ArrayList(); for(Iterator iter=layers.iterator(); iter.hasNext();){ Object item = iter.next(); if(item instanceof WebLayerInfo){ WebLayerInfo layerinfo = (WebLayerInfo)item; if(layerinfo.getName().equals(“city”)){ queryLayer.add(layerinfo); } } } List rs = query.query(ic, queryLayer); ok,用户与地图交互操作的查询就完成了,接下来我们需要从查询结果中得到这个城市的城市名。 if(rs.size() > 0){ Iterator iter = rs.iterator(); QueryResult item = (QueryResult)iter.next(); Object obj = item.getDetails().get(“name”); String startCity = obj.toString(); WebPoint start = (WebPoint)item.getHighlightGeometry(); } 这里需要解释一下的是最后一句,也许有人会问,刚才我们不是已经获取了一个point坐标了吗,那个不就是起点城市么?事实上,我们在地图上点击查询的时候不会恰好就点在表示城市的那个点的中心,WebQuery查询的时候是有一个距离容错的。而绘制航线的时候要求更精确,所以我们以查询得到的点要素的坐标为准。 费了半天劲总算把城市名给找出来了,擦一把汗,接着查,我们还需要终点城市的坐标呢!刚才我们用的是WebGeometry来查询,接下来我们将使用文本进行查询。 DbAirlineSearch dboper = new DbAirlineSearch(); String[] destinations = dboper.getDestinations(startCity); //终点城市的坐标 WebPoint[] ends = new WebPoint[destinations.length]; //航线 WebPath[] airlines = new WebPath[destinations.length]; //在WebGraphics中绘制航线所需的要素 GraphicElement[] linesElements = new GraphicElement[destinations.length]; for(int i=0;i searchFields = new ArrayList(); searchFields.add(“name”); tc.setSearchFields(searchFields); tc.setSearchText(destinations[i]); List rs2 = query.query(tc,queryLayer); //处理文本查询的结果 if(rs2.size() > 0){ QueryResult destination = (QueryResult)rs2.iterator.next(); ends[i] = (WebPoint)destination.getHighlightGeometry(); //得到一个终点坐标以后就可以绘制一条航线了 List pointList = new ArrayList(); pointList.add(start); pointList.add(ends[i]); airlines[i] = new WebPath(pointList); WebPolyline polyline = new WebPolyline(); polyline.addPath(airlines[i]); linesElement[i] = new GraphicElement(); linesElement[i].setGeometry(polyline);//这里需要说明一下,如果这里用的是WebPath,将不会绘制出航线,换成WebPolyline以后就可以了。 linesElement[i].setSymbol(lineSymbol); graphics.addGraphics(linesElement[i]); } } 好了,getAirlines(MapEvent event)方法大致就是这样,完成以后就可以在地图上进行交互查询了,选择某个城市以后就可以自动绘制出该城市出发的航线。 但是玩了几次以后,发现这个demo还可以有些扩展。比如可以将查询的结果保存在WebResults中,可以让用户点击某个航线记录,然后显示出该航线的相关信息,像起飞时间、到达时间、票价等等;另外,用户可能还有兴趣高亮显示某条记录,甚至用用ZoomTo功能啊。 仔细考虑一下,其实刚才我们已经做了很多准备工作了呀。首先,已经查询得到了起点和终点城市,根据这两个城市的名称我们就可以从数据库中查询到这条航线的其他相关信息;其次,绘制航线时创建了GraphicElement来表示线要素,这个线要素中包含了WebPolyline的信息,可以用来进行高亮和ZoomTo等操作。哈哈,现在我们来充分利用这些已有数据。 首先要创建一个辅助类,用来记录每次查询操作的结果: public class SearchAirlinesTaskResult{ private static WebSimpleLineSymbol highlightSymbol; private WebContext ctx; private GraphicElement originElement; //航线的原始符号 private WebPolyline polyline; private GraphicElement highlightElement; //高亮线的符号 private String startCity; private String endCity; private Map details; //航线的详细信息 static{ highlightSymbol = new WebSimpleLineSymbol(); highlightSymbol.setWidth(2); highlightSymbol.setColor(“234,244,34”); highlightSymbol.setLineType(WebSimpleLineSymbol.SOLID); } public SearchAirlinesTaskResult(WebContext context, GraphicElement originElement){ this.ctx = context; this.originElement = originElement; this.polyline = (WebPolyline)originElement.getGeometry(); } public Map getDetails(){ if(details == null){ DbAirlineSearch dboper = new DbAirlineSearch(); details = dboper.getDetails(startCity, endCity); } return details; } //startCity和endCity的getter与setter 方法 …… public String getDescription(){ return startCity + “ 至 ” + endCity; } //highlight的实现方法就是从WebGraphics中删除原有的线型符号,用新的高亮线型符号重绘一次 public void highlight(){ WebGraphics graphics = ctx.getWebGraphics(); graphics.removeGraphics(originElement); highlightElement = new GraphicElement(); highlightElement.setGeometry(polyline); highlightElement.setSymbol(highlightSymbol); graphics.addGraphics(highlightElement); ctx.refresh(); } public void clearHighlight(){ WebGraphics graphics = ctx.getWebGraphics(); if(highlightElement != null){ graphics.removeGraphics(highlightElement); } graphics.addGraphics(originElement); ctx.refresh(); } //根据polyline获取其外包矩形,然后刷新地图 public void zoomTo(){ WebExtent ext = getExtent(polyline); ctx.getWebMap().setCurrentExtent(ext); ctx.refresh(); } } 有了这个辅助类以后,只需要在前面的getAirlines(MapEvent event)方法中添加以下代码: …… ArrayList results = new ArrayList(); for(int i=0;i searchFields = new ArrayList(); searchFields.add(“name”); tc.setSearchFields(searchFields); tc.setSearchText(destinations[i]); List rs2 = query.query(tc,queryLayer); //处理文本查询的结果 if(rs2.size() > 0){ QueryResult destination = (QueryResult)rs2.iterator.next(); ends[i] = (WebPoint)destination.getHighlightGeometry(); //得到一个终点坐标以后就可以绘制一条航线了 List pointList = new ArrayList(); pointList.add(start); pointList.add(ends[i]); airlines[i] = new WebPath(pointList); WebPolyline polyline = new WebPolyline(); polyline.addPath(airlines[i]); linesElement[i] = new GraphicElement(); linesElement[i].setGeometry(polyline);//这里需要说明一下,如果这里用的是WebPath,将不会绘制出航线,换成WebPolyline以后就可以了。 linesElement[i].setSymbol(lineSymbol); graphics.addGraphics(linesElement[i]); //将查询结果添加到QueryResult中 SearchAirlinesTaskResult searchRs = new SearchAirlinesTaskResult(ctx,linesElement[i]); searchRs.setFromcity(fromcity); searchRs.setDestination(destinations[i]); results.add(searchRs); } } HashMap actions = new HashMap(); actions.put("高亮显示", "highlight"); actions.put("取消高亮", "clearHighlight"); actions.put("ZoomTo", "zoomTo"); ctx.getWebResults().addResultsWithActionMap("航线查询结果", results, "getDescription", "getDetails", actions); ctx.refresh(); 最终的效果图如下: 24、ArcGis Server 中 如何在Task中实现 下拉列表和 checkbox 和 radiobutton.(完整例子) 如何在Task中实现 下拉列表和 checkbox 和 radiobutton? checkbox目前还不支持。要实现下拉表和radiobutton,需要预先设置一个Map类型的选择项,此外还需要为task设置一个taskInfo,比如在自定义的task中有这样一个参数: ------------------CustomTask.java------------------------- private String param; public void setParam(String param){ this.param = param; } public void getParam(){ return this.param; } //设置下拉框选项 public Map getParams(){ java.util.Map result = new java.util.LinkedHashMap(); result.put("key1","value1"); result.put("key2","value2"); return result; } ------------------CustomTask.java------------------------- 然后在taskInfo中为其设置选择方法: ------------------CustomTaskInfo.java------------------------- TaskParamDescriptor[] taskParams = new TaskParamDescriptor[1]; taskParams[0] = new TaskParamDescriptor(CustomTask.class,"param","参数","getParams",true); //最后一个参数如果设为true,就是radioButton,如果设为false,就是下拉框 ------------------CustomTaskInfo.java------------------------- 25、ArcGIS Server开发——标注 [C#写法] private void LabelField(IFeatureLayer pFeatureLayer,IServerContext pServerContext) { IGeoFeatureLayer pGeoFeatureLayer = pFeatureLayer as IGeoFeatureLayer; pGeoFeatureLayer.AnnotationProperties.Clear(); IAnnotateLayerPropertiesCollection pAnnoLayerPropsColl = pGeoFeatureLayer.AnnotationProperties; ILabelEngineLayerProperties pLabelEngine; pLabelEngine = pServerContext.CreateObject("esriCarto.LabelEngineLayerProperties") as ILabelEngineLayerProperties; pLabelEngine.Expression = "[Field]"; IAnnotateLayerProperties pAnnoLayerProps = pLabelEngine as IAnnotateLayerProperties; pAnnoLayerPropsColl.Add(pAnnoLayerProps); pGeoFeatureLayer.DisplayAnnotation = true; 26、ArcGIS Server Java开发--Born for SOA系列 前言 在这一章里面,我们将用大量的篇幅,阐述ArcGIS Server对SOA的支持,我们在这里所指的SOA,是基于Web Serivces的SOA,其他架构的SOA不在我们这个系列的讨论之列。本章内容包括Web Service基础;如何在J2EE下环境下构建和部署Web Service;如何使用ArcGIS Server 构建 Web Service;如何在SOA系统中利用这些Web Service等。最后会以一个实际的例子为例,如何在利用ArcGIS Server编写Web Service,并且部署到SOA环境中,解决实际中的问题。本章所有的例子都是基于JDK 1.5,AXIS2,ArcGIS Server 9.2 JAVA ADF开发。本章也是CJ编写的ESRI内部教材Server Java开发教程的第十五章。 27、ArcGIS Server Java开发--Born for SOA系列 Web Service基础 Web Service是描述一些操作(利用标准化的 XML 消息传递机制可以通过网络访问这些操作)的接口。Web 服务是用标准的、规范的XML 概念描述的,称为 Web 服务的服务描述。这一描述囊括了与服务交互需要的全部细节,包括消息格式(详细描述操作)、传输协议和位置。该接口隐藏了实现服务的细节,允许独立于实现服务基于的硬件或软件平台和编写服务所用的编程语言使用服务。这允许并支持基于 Web 服务的应用程序成为松散耦合、面向组件和跨技术实现。Web 服务执行一项特定的任务或一组任务。Web 服务可以单独或同其它 Web服务一起用于实现复杂的功能或商业交易。 听起来很复杂,但是我们用一个简单的例子加以说明。这个例子跟GIS无关,但是可以帮助大家了解在J2EE环境中,整个Web Service的编写,部署过程。并且如何在.Net环境和Java环境中调用这个Web Service。 以下的需求来自于一个我原来导师的项目,这虽然不是一个典型的WebService充满前景的一个例子,但是确实用web service来解决问题的一个好的开始。项目的背景是要帮用户部署多个网站,为了节约成本,两个网站都是使用租用的空间,结果有一个租用的Linux操作系统的空间没有安装XWindows,导致一个使用JAVA写的图像缩放程序无法执行,当然,没有Xwindows,基本上需要访问AWT包的程序都会无法执行。怎么解决呢,刚好另外一台服务器上有Xwindows,可以使用这个功能,所以我们可以在另外一台服务器上编写一个WebService,发送一个图像Resize请求,请求参数是被需要被Resize的图片的URL,以及Resize的比例,而返回的结果是Resize后的图像。压缩图像的代码如下所示: import java.awt.image.BufferedImage; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import javax.imageio.ImageIO; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; /** * * @author CJ * */ public class CompressLocal { publicCompressLocal() { super(); } /** * * @param filename 要被缩放的文件名,如果是URL,请设置isURL为true * @param newname 缩放后的新的文件名 * @param std_img_w 目标文件的宽度 * @param std_img_h 目标文件的高度 * @param stretch 是否拉伸,如果1,则按照参数比例,如果是2,则按照宽度的比例;如果3,则取小的那个比例尺 * @param isURL 是否是URL */ public void doScaleOnImage(String filename,String newname,int std_img_w,intstd_img_h,int stretch,boolean isURL) { BufferedImageoldimage=null; try{ intnew_w=-1;int new_h=-1; if(isURL){ InputStreamin=(new URL(filename)).openStream(); oldimage= ImageIO.read(in); in.close(); }else{ oldimage = javax.imageio.ImageIO.read(newjava.io.File(filename)); } intold_w = oldimage.getWidth(null); //原图像的宽度 intold_h = oldimage.getHeight(null);//原图像的高度 if(stretch==1){ new_w=std_img_w; new_h=std_img_h; }elseif(stretch==2){ if((old_w > std_img_w) || (old_h > std_img_h)) { doubletagSize = (double)old_w / std_img_w; // 按宽度比例确定缩放比例 new_w= (int)(old_w / tagSize); //得到一个新的比例 new_h= (int)(old_h / tagSize); } } else if(stretch==3){ new_w= old_w; new_h = old_h; if((old_w > std_img_w) || (old_h > std_img_h)) { //如果原图像高度和宽度都大于目标高度和宽度 floattmpdouble = std_img_w; floattagSize = old_w / tmpdouble; // 按宽度比例确定缩放比例 tmpdouble= std_img_h; if(tagSize < (old_h / tmpdouble)) //按高度确定缩放比例 tagSize= old_h / tmpdouble; new_w= Math.round(old_w / tagSize); //得到一个新的比例 new_h= Math.round(old_h / tagSize); } } BufferedImagetag = new BufferedImage(new_w,new_h,BufferedImage.TYPE_INT_RGB); tag.getGraphics().drawImage(oldimage,0,0,new_w,new_h,null); oldimage.flush(); FileOutputStreamnewfile = new FileOutputStream(newname); JPEGImageEncoderencoder = JPEGCodec.createJPEGEncoder(newfile); encoder.encode(tag); tag.flush(); newfile.close(); }catch (Exception e) { e.printStackTrace(); } } /** * @param args */ public static void main(String[] args) { CompressLocalcl = new CompressLocal(); cl.doScaleOnImage("D:\\temp\\photo\\0016.jpg","c:\\temp\\cj.jpg", 535, 312,2,false); } } 缩放图片的代码已经完成,下面来写一个方法,如何调用图片缩放代码: package edu.zju; /** * 作为一个application scope的web service,供大家使用 * * @author CJ * */ public class CompressWebService { publicCompressWebService() { super(); } /** * * @param url 源jpg文件的URL * @param width 目标jpg文件的宽度 * @param length 目标jpg文件的高度 * @return 返回目标jpg文件的url */ publicString doCompress(String url,int width,int height){ StringrltURL="c:/temp/aa.jpg"; //例子程序使用,正式代码根据url生成文件名 CompressLocalcomp=new CompressLocal(); comp.doScaleOnImage(url,rltURL,width,height,1,true); returnrltURL; } /** * @param args */ public static void main(String[] args) { } } 代码编写完毕后,可以直接部署了,由于有了AXIS,部署变得非常的简单,我们使用下面的service.xml进行部署,具体的部署方法可以参考AXIS的部署文档: ScaleJPEG Service edu.zju.CompressWebService 部署完成后,可以使用下面的URL对WebService进行访问。 http://chunjie:8088/axis2/services/CompressService/doCompress?url=http://chunjie:8088/jpg/10/deans/0037.JPG&width=55&height=55 返回结果为 c:/temp/aa.jpg 如果访问http://chunjie:8088/axis2/services/CompressService?wsdl,可以得到描述当前WebService的wsdl文件。第一个WebService就部署完成了,是不是简单的不可思议?当然,得益于axis的伟大工作,隐藏了大量的细节,从而使工作变得如此简单。 刚才我们直接通过URL已经可以访问webservice了。但是我们一般在javacode里面访问,访问后还需要做一些其他的操作,比如马上把图片文件保存到服务器上的某个目录中。如果需要在Javacode中访问该web service,我们需要根据wsdl文件生成一个本地的Java代理类,通过这个代理类用来访问WebService的方法。Axis提供了wsdl2java的工具可以根据wsdl的描述生成java代理类,可以使用命令行的工具,或者使用Axis为Eclipse提供的工具生成代理类。生成代理后,就可以用下面的方法调用该WebService: DoCompress dc = newDoCompress(); dc.setUrl("http://chunjie:8088/jspsamples/jpg/thanks.jpg"); dc.setHeight(100); dc.setWidth(100); CompressWebServiceCompressWebServiceSOAP11PortStubstub = new CompressWebServiceCompressWebServiceSOAP11PortStub( null,"http://chunjie:8088/axis2/services/CompressService"); DoCompressResponsedcr=stub.doCompress(dc); System.out.println(dcr.get_return()); } catch (AxisFault e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } 当然,我们也可以用.Net来调用这个WebService,这正是Web Service的妙处所在,在VisualStudio里面新建一个项目,然后添加Web引用,在引用地址里面填入service的URL,入下图所示: 添加引用后,就可以用下面的代码来进行访问: dc.url = "http://chunjie:8088/jspsamples/jpg/thanks.jpg"; dc.height = 160; dc.width = 120; consumeWS.chunjie.CompressServicecs = new consumeWS.chunjie.CompressService(); consumeWS.chunjie.doCompressResponsedr= cs.doCompress(dc); 现在我们已经知道如何编写,部署,在JAVA和.NET中调用WebService。在下一节中,我们来看看可以通过怎样的方式对ArcGIS Server提供的WebService的进行访问。 28、ArcGIS Server Java讲座:Born For SOA--Server对于SOAP的支持 ArcGIS Server的服务可以通过Local或者Internet两种方式进行连接,Local方式直接连接到SOM上,本地的对象如何和SOM进行交互呢?事实上是通过AO进行交互的,所以你必须在本地有AO对象才可以进行连接,我们平常使用ArcCatlog就可以这样连接,或者通过Engine也可以进行这样的连接。而Internet方式直接连接到Web Service的引用地址,它是通过本地对象连接的,对于Java ADF而言,本地对象表示连接ArcGIS Server的类存在于本地JRE中。Local连接可以改变Server Object的状态,而Internet连接由于只是通过SOAP API进行访问,所有的交互都是无状态的。 对于SOAP的支持是在AO这一级,所以处理每一次SOAP请求,都会需要AO的介入。有两个接口特别要注意一下,一个是IServiceCatalogAdmin,一个是IRequestHandler,IRequestHandler我们已经在上面一节提到过,而IserviceCatalogAdmin是用来得到Server Object所提供服务的WSDL的。 SOM用来向客户端提供WSDL,所有支持的服务类型的WSDL保存在ArcGIS Server的XMLSchema目录下。 我们可以使用IServiceCatalogAdmin接口得到所有服务类型的WSDL,如下面的代码所示: private void getWSDL(String domain,String username,String password,String myserver){ ServerInitializer initializer = new ServerInitializer(); initializer.initializeServer(domain,username, password); ServerConnection gisconnection; try { gisconnection = new ServerConnection(); gisconnection.connect(myserver); // Get reference to ServerObjectManager class. IServerObjectManager som =gisconnection.getServerObjectManager(); IServerContext serverContext = som.createServerContext("usa", "MapServer"); IServiceCatalogAdmin2 isc = (IServiceCatalogAdmin2)serverContext.createObject(ServiceCatalog.getClsid()); // Catalog WSDL byte[] bitscatalog = isc.getCatalogDescriptionDocument("Catalog","http://localhost"); String catalog_wsdl = new String(bitscatalog,"UTF8"); System.out.println(catalog_wsdl); // Service WSDL byte[] bitsservice =isc.getDescriptionDocument("usa", "MapServer","http://localhost"); String service_wsdl = new String(bitsservice, "UTF8"); System.out.println(service_wsdl); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } 事实上,ArcGISServer每一个ServerObject都实现了IRequestHandler接口,都可以响应SOAP的请求,可以使用下面的方法得到ServerObject的IRequestHandler,并且得到当前地图的Default Map Name. private void getMapName(IServerContext serverContext){ IRequestHandler irh; try { irh =(IRequestHandler)serverContext.getServerObject(); String soap_request =""; soap_request +=""; soap_request +=""; soap_request +=""; soap_request +=""; soap_request +=""; soap_request +=""; String soap_response =irh.handleStringRequest("map,query,data", soap_request); } catch (AutomationException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } 所以,ArcGIS Server处理SOAP请求的能力是与生俱来的,从Server Object诞生那一天起,它就能处理SOAP请求。从而使通过Web Service的方式调用GIS功能变得异常简单。当然,我们一般都不会想Server Object直接发送请求,根据WSDL的描述,通过发送和接受SOAP请求来访问ArcGIS Server工作量十分巨大,那么我们来看看如何在ADF中使用Web Service。 29、Server Java开发--Born for SOA系列 通过代理类访问ArcGIS Server Web Service 我们现在知道,ArcGIS Server的处理SOAP请求的能力在Server Object级别就已经实现了,所以我们可以不通过Web Service的方式,而通过直接发请求到Server Object的方式实现开发,只是这样太过麻烦。 好在ArcGISServer在发布每一个Service的时候,也同时发布了一个Web Service,使我们可以通过Web Services的方式来直接访问Service。图16.1就演示了一个Map Service的WebService入口: web_service_url.jpg (54.67 KB) 2007-5-11 09:26 这是ArcGISServer提供的Web Service的引用地址,在第一节里面我们已经知道,我们可以通过在JAVA中创建代理类和.NET中添加Web引用的方式访问这个Web服务。当然,最后对于SOAP的请求的处理还是通过Server Object来执行的。           在使用ESRI提供的类包之前,我们不妨自己来试试,通过特定的工具来根据WSDL生成本地代理类。AXIS2提供的WSDL2JAVA是一个很好的工具,生成的JAVA类的代码多达7万行。里面包含了所有需要访问这个Web Service所需要的一切类和方法。这些方法包括Identify,Find,QueryFeatureCount等,这些方法的参数和返回值,也作为内部类包含在该方法所在类中。需要特别指出的是:在访问ArcGIS Server的Web Service的时候,在本地JRE环境中,只需要有AXIS的类库和标准的JRE库即可,不需要任何其他的AO类或者代理类,这极大地提高了部署的灵活性。那么我们使用代理类来调用该Web Services的一个QueryFeatureCount的一个功能:  packageedu.zju.esrisvs; importjava.rmi.RemoteException; import org.apache.axis2.AxisFault; import edu.zju.esrisvs.World_MapServerStub.QueryFeatureCount; import edu.zju.esrisvs. World _MapServerStub.QueryFeatureCountResponse; import edu.zju.esrisvs. World _MapServerStub.QueryFilter; public classWebSvsTest { public WebSvsTest() { super(); } /**           * @param args           */          public static void main(String[] args){ try { World _MapServerStubstub=new World _MapServerStub(); QueryFilter qf=newQueryFilter(); QueryFeatureCountqfc=new QueryFeatureCount(); qfc.setLayerID(0); qfc.setMapName("Layers"); qf.setWhereClause("1=1"); qfc.setQueryFilter(qf); QueryFeatureCountResponseqfr=stub.QueryFeatureCount(qfc); System.out.println(qfr.getResult()); } catch (AxisFault e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } } } 这就是使用代理类来访问该Web Service的过程,用工具生成的代理类非常长,大概有几万行代码,我就不贴在这里了,大家可以用工具生成。大家可能注意到,很多类的名字如QueryFitler,和方法,如QueryFeatureCount,都跟ADF里面的一样,事实上ADF里面访问Web Service的包同样也是用AXIS生成的。你也可以在.NET里面,通过添加Web引用的方式来访问该ServerObject的Web Service。

下载文档到电脑,查找使用更方便

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档