四 QueryResult
上面几个小节让我们知道了不同的查询功能应该怎么去实现,每个查询中都返回了QueryResult对象的集合。这个小节我们就来详细说说QueryResult对象。
QueryResult对象是一个查询结果,当然,这是一个服务器端对象,然而在服务器上QueryResult并不仅仅是一个记录,而是一个可以和Map互动,可以高亮显示、移除、缩放到的一个对象。
比如当我们做完查询以后,想在地图上显示所有的结果要素,那么,调用QueryResult的highlight方法就可以做到:
webContext.getWebGraphics().clearGraphics();
for (int i = 0; i <>
{
QueryResult queryResult = listQueryResult.get(i);
queryResult.highlight();
}
webContext.refresh();
上面的代码遍历了查询结果中所有的QueryResult对象,调用每个对象的highlight方法使其在地图上高亮显示,让我们看一下执行的结果:
高亮的这些元素事实上是将这些查询结果以Graphic的形式添加到Map中的。ADF的Graphic通常用于表现临时的、动态生成的业务数据,下面一章我们会马上去看Graphic的来龙去脉。
2009年5月14日星期四
ArcGIS Server Java ADF 案例教程 26
三 Where条件查询
三个实现了QueryCriteria接口的类我们还剩PredefinedQueryCriteria没有介绍,这个类是用于进行where查询的。在进行查询的时候,通常需要设置一个比较复杂的过滤条件,这里就需要PredefinedQueryCriteria。
下面我们尝试把前面使用TextCriteria查询的方法用PredefinedQueryCriteria来实现:
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
PredefinedQueryCriteria criteria = new PredefinedQueryCriteria();
criteria.setWhereClause("CITY_NAME like '%"+this.keyWord+"%'");
List listQueryResult = webQuery.query(criteria, webQuery.getQueryLayers());
PredefinedQueryCriteria的用法非常简单,无非是把你想要设置的where条件放到它的whereClause属性中去,这里就不赘述了。
三个实现了QueryCriteria接口的类我们还剩PredefinedQueryCriteria没有介绍,这个类是用于进行where查询的。在进行查询的时候,通常需要设置一个比较复杂的过滤条件,这里就需要PredefinedQueryCriteria。
下面我们尝试把前面使用TextCriteria查询的方法用PredefinedQueryCriteria来实现:
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
PredefinedQueryCriteria criteria = new PredefinedQueryCriteria();
criteria.setWhereClause("CITY_NAME like '%"+this.keyWord+"%'");
List
PredefinedQueryCriteria的用法非常简单,无非是把你想要设置的where条件放到它的whereClause属性中去,这里就不赘述了。
ArcGIS Server Java ADF 案例教程 25
二 几何对象查询
在上一章的QueryTask中也有对几何对象的查询,你可以回忆一下那个Task面板中“选择”那个Tool。这个Tool会在Map中绘制一个多边形,然后ADF根据这个多边形查询位于多边形内的所有要素。
几何对象的查询主要使用IdentifyCriteria对象,下面的代码实现了这样一个根据多边形查询要素的功能:
WebContext webContext = event.getWebContext();
WebGeometry webGeometry = (WebPolygon) event.getWebGeometry().toMapGeometry(webContext.getWebMap());
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
List layers = webQuery.getQueryLayers();
List queryLayers = new ArrayList();
for (WebLayerInfo layer : layers)
{
if (layer.getName().equals("World Cities"))
{
queryLayers.add(layer);
break;
}
}
IdentifyCriteria identifyCriteria = new IdentifyCriteria();
identifyCriteria.setWebGeometry(webGeometry);
List listQueryResult = webQuery.query(identifyCriteria, queryLayers);
在这个查询过程中,首先从MapEvent事件中获得从客户端传来的WebGeometry对象(这里是用户在客户端的Map中画的多边形),这个对象是基于客户端坐标的,因此需要转化成地图坐标。由于我们只想查“World Cities”这个图层上的要素,因此下面又构造了一个queryLayers,将我们想要查询的图层挑选了出来。在此之后,我们就可以使用IdentifyCriteria对象,将ADF转化成地图坐标的WebGeometry对象作为它的参数,进行查询得到结果。
在上一章的QueryTask中也有对几何对象的查询,你可以回忆一下那个Task面板中“选择”那个Tool。这个Tool会在Map中绘制一个多边形,然后ADF根据这个多边形查询位于多边形内的所有要素。
几何对象的查询主要使用IdentifyCriteria对象,下面的代码实现了这样一个根据多边形查询要素的功能:
WebContext webContext = event.getWebContext();
WebGeometry webGeometry = (WebPolygon) event.getWebGeometry().toMapGeometry(webContext.getWebMap());
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
List
List
for (WebLayerInfo layer : layers)
{
if (layer.getName().equals("World Cities"))
{
queryLayers.add(layer);
break;
}
}
IdentifyCriteria identifyCriteria = new IdentifyCriteria();
identifyCriteria.setWebGeometry(webGeometry);
List
在这个查询过程中,首先从MapEvent事件中获得从客户端传来的WebGeometry对象(这里是用户在客户端的Map中画的多边形),这个对象是基于客户端坐标的,因此需要转化成地图坐标。由于我们只想查“World Cities”这个图层上的要素,因此下面又构造了一个queryLayers,将我们想要查询的图层挑选了出来。在此之后,我们就可以使用IdentifyCriteria对象,将ADF转化成地图坐标的WebGeometry对象作为它的参数,进行查询得到结果。
2009年5月8日星期五
ArcGIS Server Java ADF 案例教程 24
第六章 查询及结果
对空间信息进行查询是GIS中很基本且很常用的功能,在这一章中,我们主要看在ADF中如何进行查询、查询出结果以后怎样返回客户端。另外,在这里也先预告下,在下一章我们还会讲查询到的这些结果怎样在Map中表现出来。
一 文本查询
在上一章QueryTask的query方法中我们已经用到了文本查询的功能,你可以回忆一下,在这个方法中我们根据Task面板中文本框的内容,对图层进行了查询,用到的就是keyWord这个字符串变量。下面我们来详细说一下文本查询的过程。
在ADF中负责进行文本查询的是WebQuery对象,需要用到查询功能的ADF应用都需要在配置文件中加上了这个对象:

当我们需要使用查询功能的时候,首先第一步就是获得这个WebQuery对象,当然,通常都是先获得WebContext对象,再查找它的attribute来获得WebQuery对象:
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
通过这个WebQuery对象我们就可以开始进行查询了,WebQuery最重要的方法就是query方法,让我们先看一下query方法的原型:

这个query方法主要有两个参数:criteria参数表示查询条件,这是一个实现了QueryCriteria 接口的对象,实现这个接口的类有三种:IdentifyCriteria, PredefinedQueryCriteria, TextCriteria,分别对应不同的查询类型,在这里我们将要用到TextCriteria这个类;query方法的另外一个参数queryLayers表示需要进行查询的图层,一个服务中可能有多个图层,这个参数可以限制查询的范围。
上面说到进行查询需要一个实现了QueryCriteria接口的对象作为查询条件,这里我们需要查询文本,所以需要使用TextCriteria,这是一个专为文本查询设计的类。下面的代码定义了一个TextCriteria对象,通过这个对象定义了一些查询条件和限制,最后通过WebQuery对象的query方法来得到查询结果:

这是一个很简单的查询,事实上TextCriteria还可以设置很多限制和条件,比如限制查询字段、是否模糊查询、设置返回字段、设置LayerDefinition (包括字段别名、符号渲染方式)等等。最后,查询后会得到一个QueryResult的集合,关于这个QueryResult的特点和用法,我们在后面会有一个小节专门说明。
对空间信息进行查询是GIS中很基本且很常用的功能,在这一章中,我们主要看在ADF中如何进行查询、查询出结果以后怎样返回客户端。另外,在这里也先预告下,在下一章我们还会讲查询到的这些结果怎样在Map中表现出来。
一 文本查询
在上一章QueryTask的query方法中我们已经用到了文本查询的功能,你可以回忆一下,在这个方法中我们根据Task面板中文本框的内容,对图层进行了查询,用到的就是keyWord这个字符串变量。下面我们来详细说一下文本查询的过程。
在ADF中负责进行文本查询的是WebQuery对象,需要用到查询功能的ADF应用都需要在配置文件中加上了这个对象:

当我们需要使用查询功能的时候,首先第一步就是获得这个WebQuery对象,当然,通常都是先获得WebContext对象,再查找它的attribute来获得WebQuery对象:
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
通过这个WebQuery对象我们就可以开始进行查询了,WebQuery最重要的方法就是query方法,让我们先看一下query方法的原型:

这个query方法主要有两个参数:criteria参数表示查询条件,这是一个实现了QueryCriteria 接口的对象,实现这个接口的类有三种:IdentifyCriteria, PredefinedQueryCriteria, TextCriteria,分别对应不同的查询类型,在这里我们将要用到TextCriteria这个类;query方法的另外一个参数queryLayers表示需要进行查询的图层,一个服务中可能有多个图层,这个参数可以限制查询的范围。
上面说到进行查询需要一个实现了QueryCriteria接口的对象作为查询条件,这里我们需要查询文本,所以需要使用TextCriteria,这是一个专为文本查询设计的类。下面的代码定义了一个TextCriteria对象,通过这个对象定义了一些查询条件和限制,最后通过WebQuery对象的query方法来得到查询结果:

这是一个很简单的查询,事实上TextCriteria还可以设置很多限制和条件,比如限制查询字段、是否模糊查询、设置返回字段、设置LayerDefinition (包括字段别名、符号渲染方式)等等。最后,查询后会得到一个QueryResult的集合,关于这个QueryResult的特点和用法,我们在后面会有一个小节专门说明。
2009年4月30日星期四
ArcGIS Server Java ADF 案例教程 23
三 Task的渲染
Task的功能类中还有一个属性,IDE默认生成的名字是taskInfo,它对应这个另外一个Java类(如上面QueryTaskTaskInfo类)的对象。这个类主要控制Task的渲染,我们可以回想一下,如果光在Task的功能类中定义了Command是一个void类型的query方法,Task面板怎么会生成一个标签为“查询”的按钮出来的呢?答案就在taskInfo中。
打开上面生成的“QueryTaskTaskInfo.java”文件,我们可以看到以下的内容:
public class QueryTaskTaskInfo extends SimpleTaskInfo
{
public TaskDescriptor getTaskDescriptor()
{
TaskDescriptor descriptor = new TaskDescriptor(QueryTask.class, "queryTask", "查询任务");
return descriptor;
}
public TaskParamDescriptorModel[] getParamDescriptors()
{
TaskParamDescriptor[] descriptors = new TaskParamDescriptor[1];
descriptors[0] = new TaskParamDescriptor(QueryTask.class, "keyWord", "关键词", "getKeyWord", "setKeyWord");
return descriptors;
}
public TaskActionDescriptorModel[] getActionDescriptors()
{
TaskActionDescriptor[] descriptors = new TaskActionDescriptor[1];
descriptors[0] = new TaskActionDescriptor(QueryTask.class, "query", "查询");
return descriptors;
}
public TaskToolDescriptorModel[] getToolDescriptors()
{
TaskToolDescriptor[] descriptors = new TaskToolDescriptor[1];
descriptors[0] = new TaskToolDescriptor(QueryTask.class, "select", "选择", "EsriMapPolygon");
return descriptors;
}
}
简单的说,TaskInfo类中包含4个主要的属性:TaskDescriptor对象、TaskParamDescriptor[]数组、TaskActionDescriptor[]数组、TaskToolDescriptor[]数组。很好理解,TaskDescriptor对应整个Task的外观,比如Task的标题等;TaskParamDescriptor[]描述所有Parameter的外观;TaskActionDescriptor[]描述所有Command的外观;TaskToolDescriptor[]描述所有Tool的外观。一个Task中肯定包含不定数目的Parameter、Command或Tool,因此他们的描述需要数组来存放。
让我们通过TaskToolDescriptor来更详细地看一下,上面的代码设置“select”这个TaskTool表现为一个“选择”按钮,并在点击时通知Map执行“EsriMapPolygon”操作。现在我想让它渲染成一个自定义的图片按钮,并随鼠标移上或点击切换不同的图片,用下面的代码可以实现这样的功能:
descriptors[0].setRendererType(TaskToolDescriptor.IMAGE_RENDERER_TYPE);
descriptors[0].setDefaultImage("images/tasks/maptools/polygon.gif");
descriptors[0].setHoverImage("images/tasks/maptools/polygonU.gif");
descriptors[0].setSelectedImage("images/tasks/maptools/polygonD.gif");
descriptors[0].setDisabledImage("images/tasks/maptools/polygonX.gif");
现在我还想调整一下这些按钮、文本框的布局,怎么办?TaskInfo还可以指定Task各个子元素的布局,让我们在刚刚的QueryTaskTaskInfo类中再添加以下的代码:
public TaskLayout[] getTaskLayout()
{
TabularLayout[] descriptors = new TabularLayout[1];
descriptors[0] = new TabularLayout();
descriptors[0].addComponent(getParamDescriptors()[0], new TabularPosition(0, 0, 0, 0));
descriptors[0].addComponent(getActionDescriptors()[0], new TabularPosition(1, 0, 0, 0));
descriptors[0].addComponent(getToolDescriptors()[0], new TabularPosition(2, 0, 0, 0));
return descriptors;
}
经过上面的两次修改,我们会发现Task面板的Tool变成了使用图片渲染,并且布局方式更加我们的定义进行了变化,效果如图 17所示。
顺便回头再看看页面中Task的定义,其中有taskInfo属性,它对应的就是这个Task的TaskInfo对象:
taskInfo="#{queryTask.taskInfo}"
最后稍微总结一下吧,Task的外观主要通过TaskInfo来定义,ADF会根据Task所对应的TaskInfo,通过XSL样式文件渲染成DHTML代码,并输出到浏览器。到现在为止,一个Task面板包含什么内容、对应后台什么对象、怎么控制它们的表现,你应该都清楚了吧?
Task的功能类中还有一个属性,IDE默认生成的名字是taskInfo,它对应这个另外一个Java类(如上面QueryTaskTaskInfo类)的对象。这个类主要控制Task的渲染,我们可以回想一下,如果光在Task的功能类中定义了Command是一个void类型的query方法,Task面板怎么会生成一个标签为“查询”的按钮出来的呢?答案就在taskInfo中。
打开上面生成的“QueryTaskTaskInfo.java”文件,我们可以看到以下的内容:
public class QueryTaskTaskInfo extends SimpleTaskInfo
{
public TaskDescriptor getTaskDescriptor()
{
TaskDescriptor descriptor = new TaskDescriptor(QueryTask.class, "queryTask", "查询任务");
return descriptor;
}
public TaskParamDescriptorModel[] getParamDescriptors()
{
TaskParamDescriptor[] descriptors = new TaskParamDescriptor[1];
descriptors[0] = new TaskParamDescriptor(QueryTask.class, "keyWord", "关键词", "getKeyWord", "setKeyWord");
return descriptors;
}
public TaskActionDescriptorModel[] getActionDescriptors()
{
TaskActionDescriptor[] descriptors = new TaskActionDescriptor[1];
descriptors[0] = new TaskActionDescriptor(QueryTask.class, "query", "查询");
return descriptors;
}
public TaskToolDescriptorModel[] getToolDescriptors()
{
TaskToolDescriptor[] descriptors = new TaskToolDescriptor[1];
descriptors[0] = new TaskToolDescriptor(QueryTask.class, "select", "选择", "EsriMapPolygon");
return descriptors;
}
}
简单的说,TaskInfo类中包含4个主要的属性:TaskDescriptor对象、TaskParamDescriptor[]数组、TaskActionDescriptor[]数组、TaskToolDescriptor[]数组。很好理解,TaskDescriptor对应整个Task的外观,比如Task的标题等;TaskParamDescriptor[]描述所有Parameter的外观;TaskActionDescriptor[]描述所有Command的外观;TaskToolDescriptor[]描述所有Tool的外观。一个Task中肯定包含不定数目的Parameter、Command或Tool,因此他们的描述需要数组来存放。
让我们通过TaskToolDescriptor来更详细地看一下,上面的代码设置“select”这个TaskTool表现为一个“选择”按钮,并在点击时通知Map执行“EsriMapPolygon”操作。现在我想让它渲染成一个自定义的图片按钮,并随鼠标移上或点击切换不同的图片,用下面的代码可以实现这样的功能:
descriptors[0].setRendererType(TaskToolDescriptor.IMAGE_RENDERER_TYPE);
descriptors[0].setDefaultImage("images/tasks/maptools/polygon.gif");
descriptors[0].setHoverImage("images/tasks/maptools/polygonU.gif");
descriptors[0].setSelectedImage("images/tasks/maptools/polygonD.gif");
descriptors[0].setDisabledImage("images/tasks/maptools/polygonX.gif");
现在我还想调整一下这些按钮、文本框的布局,怎么办?TaskInfo还可以指定Task各个子元素的布局,让我们在刚刚的QueryTaskTaskInfo类中再添加以下的代码:
public TaskLayout[] getTaskLayout()
{
TabularLayout[] descriptors = new TabularLayout[1];
descriptors[0] = new TabularLayout();
descriptors[0].addComponent(getParamDescriptors()[0], new TabularPosition(0, 0, 0, 0));
descriptors[0].addComponent(getActionDescriptors()[0], new TabularPosition(1, 0, 0, 0));
descriptors[0].addComponent(getToolDescriptors()[0], new TabularPosition(2, 0, 0, 0));
return descriptors;
}
经过上面的两次修改,我们会发现Task面板的Tool变成了使用图片渲染,并且布局方式更加我们的定义进行了变化,效果如图 17所示。
顺便回头再看看页面中Task的定义,其中有taskInfo属性,它对应的就是这个Task的TaskInfo对象:
taskInfo="#{queryTask.taskInfo}"
最后稍微总结一下吧,Task的外观主要通过TaskInfo来定义,ADF会根据Task所对应的TaskInfo,通过XSL样式文件渲染成DHTML代码,并输出到浏览器。到现在为止,一个Task面板包含什么内容、对应后台什么对象、怎么控制它们的表现,你应该都清楚了吧?
ArcGIS Server Java ADF 案例教程 22
二 Task的执行
Task的执行主要在对应的功能类(比如上面的QueryTask类)中实现,上面已经详述了Task中的Parameter、Command、Tool分别对应的功能类中的元素,下面就让我们看看怎么去使用它们。
在上面定义的那个QueryTask中,我们设计点击“查询”按钮就能根据文本框中输入的关键词去查找相应的要素。下面是实现这样功能的一段代码:
public void query(TaskEvent event)
{
WebContext webContext = event.getWebContext();
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
TextCriteria textCriteria = new TextCriteria();
textCriteria.setMaxRecordCount(100);
textCriteria.setSearchText(this.keyWord);
List listQueryResult = webQuery.query(textCriteria, webQuery.getQueryLayers());
webContext.getWebGraphics().clearGraphics();
for (int i = 0; i <>
{
QueryResult queryResult = listQueryResult.get(i);
queryResult.highlight();
}
webContext.refresh();
}
我们可以看到,从TaskEvent中可以直接获得我们的WebContext对象;从WebContext开始可以执行一系列的操作,其中用到了keyWord这个属性,keyWord对应的就是用户在Task面板的文本框中输入的文字;最后查询到结果在地图上通过Graphic高亮出来,通过WebContext的refresh方法输出到浏览器。
这里涉及到了Parameter和Command的使用;至于Tool,唯一的区别就是WebContext是从MapEvent中获得,因此这里就不详细写了。另外,这段代码中还涉及到如何进行查询、如何使用Graphic等,这些内容我们在后面会详细讲。
Task的执行主要在对应的功能类(比如上面的QueryTask类)中实现,上面已经详述了Task中的Parameter、Command、Tool分别对应的功能类中的元素,下面就让我们看看怎么去使用它们。
在上面定义的那个QueryTask中,我们设计点击“查询”按钮就能根据文本框中输入的关键词去查找相应的要素。下面是实现这样功能的一段代码:
public void query(TaskEvent event)
{
WebContext webContext = event.getWebContext();
WebQuery webQuery = (WebQuery) webContext.getAttribute("query");
TextCriteria textCriteria = new TextCriteria();
textCriteria.setMaxRecordCount(100);
textCriteria.setSearchText(this.keyWord);
List
webContext.getWebGraphics().clearGraphics();
for (int i = 0; i <>
{
QueryResult queryResult = listQueryResult.get(i);
queryResult.highlight();
}
webContext.refresh();
}
我们可以看到,从TaskEvent中可以直接获得我们的WebContext对象;从WebContext开始可以执行一系列的操作,其中用到了keyWord这个属性,keyWord对应的就是用户在Task面板的文本框中输入的文字;最后查询到结果在地图上通过Graphic高亮出来,通过WebContext的refresh方法输出到浏览器。
这里涉及到了Parameter和Command的使用;至于Tool,唯一的区别就是WebContext是从MapEvent中获得,因此这里就不详细写了。另外,这段代码中还涉及到如何进行查询、如何使用Graphic等,这些内容我们在后面会详细讲。
2009年4月29日星期三
ArcGIS Server Java ADF 案例教程 21
这一章我们主要研究一下Task同志的成份和出身。ADF中本身包含一些现成的Task,不过相比对开发人员来说没有很大的意义,这一章我们的重点还是在如何自定义Task。
Task是实现业务逻辑的重要部分。简单来说,Task可以让输入一些参数(在页面上以文本框、组合框等形式出现)、通过Command和Tool的组合,帮助你完成特定的“任务”并返回结果,这就是所谓的Task。
一 Task的请求
虽说这一节的标题是Task的请求,但在这里我们对ADF的Task怎么向服务器做请求并不作深入的探讨,这里我们主要是去了解一个Task在服务器端执行的时候需要客户端提供的信息有哪些。
如图 12是一个简单的Task请求面板,在这个面板中会有一些Parameter(一般以文本框、组合框等形式出现)和一些Command或Tool(一般以按钮形式出现)。下面我们先来定义一个Task,其中包含一个Parameter、一个Command和一个Tool。
在IDE中新建一个Task最简单的方法就是使用菜单【File】-【New】-【Task】,然后给你的Task起个名字,如图 14。
这个时候,IDE会弹出另外一个对话框供你对这个Task进行一些设置,比如Task的名称、里面包含的Parameter、Command(在这里叫Action)、Tool等。这里就根据你的需要进行填写了,我们先各添加一个,如图 15所示。
这时你会发现,在Java源文件目录下多了2个文件,一个是“QueryTask.java”,另一个是“QueryTaskTaskInfo.java”,同时在faces-config.xml文件中也多了一个名为queryTask的ManagedBean。
打开“QueryTask.java”文件,我们可以找到和刚才定义的那些内容相对应的代码,下面的表简述了这样的一种对应关系:
类型 名称
Parameter String keyWord
Command Void方法 query(TaskEvent event)
Tool Void方法 select(MapEvent event)
在“QueryTask.java”中我们还可以找到QueryTaskTaskInfo对象,关于QueryTaskTaskInfo这个类的定义及其作用我们在后面再详细说。下面让我们先把这个Task添加到页面上去:

这里的taskInfo属性就是这个Task的TaskInfo对象,我们可以先不用管,唯一一个以前没有出现过的属性就是windowingSupport,这个属性定义的是这个Task面板是否可以在页面上浮动并拖拽。让我们运行下这个页面看一下效果:
稍微总结一下,Task中主要包含3个功能元素:Parameter、Command、Tool;Task对应的Java类有2个,其中在实现功能的Task类(比如上面的QueryTask类)中,Parameter对应类中的属性字段、Command对应类中的带TaskEvent 参数的void方法、Tool对应类中的带MapEvent 参数的void方法。这样,前台的Task请求就和后台具体的功能联系起来了。
Task是实现业务逻辑的重要部分。简单来说,Task可以让输入一些参数(在页面上以文本框、组合框等形式出现)、通过Command和Tool的组合,帮助你完成特定的“任务”并返回结果,这就是所谓的Task。
一 Task的请求
虽说这一节的标题是Task的请求,但在这里我们对ADF的Task怎么向服务器做请求并不作深入的探讨,这里我们主要是去了解一个Task在服务器端执行的时候需要客户端提供的信息有哪些。
如图 12是一个简单的Task请求面板,在这个面板中会有一些Parameter(一般以文本框、组合框等形式出现)和一些Command或Tool(一般以按钮形式出现)。下面我们先来定义一个Task,其中包含一个Parameter、一个Command和一个Tool。
在IDE中新建一个Task最简单的方法就是使用菜单【File】-【New】-【Task】,然后给你的Task起个名字,如图 14。
这个时候,IDE会弹出另外一个对话框供你对这个Task进行一些设置,比如Task的名称、里面包含的Parameter、Command(在这里叫Action)、Tool等。这里就根据你的需要进行填写了,我们先各添加一个,如图 15所示。
这时你会发现,在Java源文件目录下多了2个文件,一个是“QueryTask.java”,另一个是“QueryTaskTaskInfo.java”,同时在faces-config.xml文件中也多了一个名为queryTask的ManagedBean。
打开“QueryTask.java”文件,我们可以找到和刚才定义的那些内容相对应的代码,下面的表简述了这样的一种对应关系:
类型 名称
Parameter String keyWord
Command Void方法 query(TaskEvent event)
Tool Void方法 select(MapEvent event)
在“QueryTask.java”中我们还可以找到QueryTaskTaskInfo对象,关于QueryTaskTaskInfo这个类的定义及其作用我们在后面再详细说。下面让我们先把这个Task添加到页面上去:

这里的taskInfo属性就是这个Task的TaskInfo对象,我们可以先不用管,唯一一个以前没有出现过的属性就是windowingSupport,这个属性定义的是这个Task面板是否可以在页面上浮动并拖拽。让我们运行下这个页面看一下效果:
稍微总结一下,Task中主要包含3个功能元素:Parameter、Command、Tool;Task对应的Java类有2个,其中在实现功能的Task类(比如上面的QueryTask类)中,Parameter对应类中的属性字段、Command对应类中的带TaskEvent 参数的void方法、Tool对应类中的带MapEvent 参数的void方法。这样,前台的Task请求就和后台具体的功能联系起来了。
2009年4月28日星期二
ArcGIS Server Java ADF 案例教程 20
七 自定义Button实现前面的Command和Tool【案例】
在这一章前面的小节中,我们实现了两个案例分别演示Command和Tool的功能;在这个案例里,让我们用Button来完成相同的工作。
首先,我们需要在一个我们自己定义的ManagedBean 来实现这些功能。注意,在这个ManagedBean中我们会使用WebContext对象(否则怎么调用地图功能),因此,我们这个类定义需要实现WebContextInitialize接口。当然,在IDE中有更简单的方法,比如在Eclipse中选择菜单【New】-【Context Attribute】就可以新建一个ManagedBean并添加到WebContext对象的attributes中去。
好了,现在我们已经有了自己的ManagedBean类“MapEditor”;接下来我们先实现地图等比放大的功能。在这个类中,我们添加一个不带任何参数的方法zoomIn,在zoomIn实现地图放大一倍的功能。源代码如下:
package wuyf;
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.data.WebContextInitialize;
public class MapEditor implements WebContextInitialize
{
private WebContext webContext;
public void destroy() {}
public void init(WebContext context)
{
this.webContext = context;
}
public void zoomIn()
{
webContext.getWebMap().getCurrentExtent().expand(0.5);
webContext.refresh();
}
}
在页面中用如下的标签进行定义:

这个时候,你在页面上点击这个Button的时候,地图也会放大一倍了。
下面我们接着去实现点击地图居中,和上面类似,只不过实现这个功能的方法需要带一个MapEvent参数:
public void centerAt(MapEvent event)
{
WebPoint webPoint = (WebPoint)event.getWebGeometry().toMapGeometry(webContext.getWebMap());
webContext.getWebMap().centerAt(webPoint, 1.0);
webContext.refresh();
}
在页面中定义的标签则如下:

好了,试着点击这个按钮,再在地图上点击,是不是地图以点击处居中了?
在这一章前面的小节中,我们实现了两个案例分别演示Command和Tool的功能;在这个案例里,让我们用Button来完成相同的工作。
首先,我们需要在一个我们自己定义的ManagedBean 来实现这些功能。注意,在这个ManagedBean中我们会使用WebContext对象(否则怎么调用地图功能),因此,我们这个类定义需要实现WebContextInitialize接口。当然,在IDE中有更简单的方法,比如在Eclipse中选择菜单【New】-【Context Attribute】就可以新建一个ManagedBean并添加到WebContext对象的attributes中去。
好了,现在我们已经有了自己的ManagedBean类“MapEditor”;接下来我们先实现地图等比放大的功能。在这个类中,我们添加一个不带任何参数的方法zoomIn,在zoomIn实现地图放大一倍的功能。源代码如下:
package wuyf;
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.data.WebContextInitialize;
public class MapEditor implements WebContextInitialize
{
private WebContext webContext;
public void destroy() {}
public void init(WebContext context)
{
this.webContext = context;
}
public void zoomIn()
{
webContext.getWebMap().getCurrentExtent().expand(0.5);
webContext.refresh();
}
}
在页面中用如下的标签进行定义:

这个时候,你在页面上点击这个Button的时候,地图也会放大一倍了。
下面我们接着去实现点击地图居中,和上面类似,只不过实现这个功能的方法需要带一个MapEvent参数:
public void centerAt(MapEvent event)
{
WebPoint webPoint = (WebPoint)event.getWebGeometry().toMapGeometry(webContext.getWebMap());
webContext.getWebMap().centerAt(webPoint, 1.0);
webContext.refresh();
}
在页面中定义的标签则如下:

好了,试着点击这个按钮,再在地图上点击,是不是地图以点击处居中了?
ArcGIS Server Java ADF 案例教程 19
六 Button
Button不属于Toolbar的子元素,而是和Toolbar有类似的地位;但同时,它的功能又集合了Command和Tool。Button的出现主要是为了方便用户,当用户不希望出现一个工具条来摆放与地图相关的功能,那么Button可以在页面的任意地方摆放一个与地图相关的按钮。
在ADF工程里,让我们打开ADF自带的模板页面“edit.jsp”,在里面有很多Button。以下是定义了一个在客户端绘制一个矩形,服务器端执行“选择要素”操作的Button:

我们可以看到,Button和Command以及Tool都很相像,有clientAction也有serverAction,所不同的是,Button需要指定一个mapId属性,因为Command和Tool都是存放在Toolbar中,Toolbar会有个统一的mapId来表示和哪个Map绑定;而Button则由于是独立的按钮,需要自己指定与之绑定的Map。
clientAction属性是可选的,如果你不填clientAction,那么这个Button的行为就类似于Command;如果你设置了clientAction,那么这个Button的行为就类似与Tool。另外,这里的serverAction需要指向一个服务器端对象的方法,如果没有clientAction,那么这个serverAction指向的方法应该是一个无参数的方法;如果有clientAction,那么这个serverAction指向的方法应该是一个带MapEvent 参数的方法。
Button不属于Toolbar的子元素,而是和Toolbar有类似的地位;但同时,它的功能又集合了Command和Tool。Button的出现主要是为了方便用户,当用户不希望出现一个工具条来摆放与地图相关的功能,那么Button可以在页面的任意地方摆放一个与地图相关的按钮。
在ADF工程里,让我们打开ADF自带的模板页面“edit.jsp”,在里面有很多Button。以下是定义了一个在客户端绘制一个矩形,服务器端执行“选择要素”操作的Button:

我们可以看到,Button和Command以及Tool都很相像,有clientAction也有serverAction,所不同的是,Button需要指定一个mapId属性,因为Command和Tool都是存放在Toolbar中,Toolbar会有个统一的mapId来表示和哪个Map绑定;而Button则由于是独立的按钮,需要自己指定与之绑定的Map。
clientAction属性是可选的,如果你不填clientAction,那么这个Button的行为就类似于Command;如果你设置了clientAction,那么这个Button的行为就类似与Tool。另外,这里的serverAction需要指向一个服务器端对象的方法,如果没有clientAction,那么这个serverAction指向的方法应该是一个无参数的方法;如果有clientAction,那么这个serverAction指向的方法应该是一个带MapEvent 参数的方法。
ArcGIS Server Java ADF 案例教程 18
五 自定义Tool实现点击地图居中【案例】
在这一小节中,我们通过自定义一个Tool来激活这样的地图功能:每次点击地图,都以这个点击点为中心使地图居中。
如下,我们新建了这样一个类CenterAtToolAction:
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.data.geometry.WebPoint;
import com.esri.adf.web.faces.event.MapEvent;
import com.esri.adf.web.faces.event.MapToolAction;
public class CenterAtToolAction implements MapToolAction
{
public void execute(MapEvent event) throws Exception
{
WebContext webContext = event.getWebContext();
WebPoint webPoint = (WebPoint)event.getWebGeometry().toMapGeometry(webContext.getWebMap());
webContext.getWebMap().centerAt(webPoint, 0.5);
webContext.refresh();
}
}
这个类的关键是实现了MapToolAction的execute方法,当一个Tool被激活,并在地图上进行了操作以后,一个请求就会被发送到服务器,对应的MapToolAction的execute方法就会被执行。
这里,我们通过MapEvent的获得WebContext和WebGeometry 对象。比如我们在浏览器中点击了一下,那么WebGeometry就是一个点对象,当然,这个点坐标是基于屏幕坐标的,我们需要调用它的toMapGeometry方法转化成地理坐标。
当这些准备工作完成以后,我们就可以使用WebMap的centerAt方法使地图居中,随后刷新WebContext输出响应到浏览器。
如图 11是点击居中前后的地图。
在这一小节中,我们通过自定义一个Tool来激活这样的地图功能:每次点击地图,都以这个点击点为中心使地图居中。
如下,我们新建了这样一个类CenterAtToolAction:
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.data.geometry.WebPoint;
import com.esri.adf.web.faces.event.MapEvent;
import com.esri.adf.web.faces.event.MapToolAction;
public class CenterAtToolAction implements MapToolAction
{
public void execute(MapEvent event) throws Exception
{
WebContext webContext = event.getWebContext();
WebPoint webPoint = (WebPoint)event.getWebGeometry().toMapGeometry(webContext.getWebMap());
webContext.getWebMap().centerAt(webPoint, 0.5);
webContext.refresh();
}
}
这个类的关键是实现了MapToolAction的execute方法,当一个Tool被激活,并在地图上进行了操作以后,一个请求就会被发送到服务器,对应的MapToolAction的execute方法就会被执行。
这里,我们通过MapEvent的获得WebContext和WebGeometry 对象。比如我们在浏览器中点击了一下,那么WebGeometry就是一个点对象,当然,这个点坐标是基于屏幕坐标的,我们需要调用它的toMapGeometry方法转化成地理坐标。
当这些准备工作完成以后,我们就可以使用WebMap的centerAt方法使地图居中,随后刷新WebContext输出响应到浏览器。
如图 11是点击居中前后的地图。
ArcGIS Server Java ADF 案例教程 17
四 Tool
Tool是另外一种按钮,虽然它在外观上和Command可能没什么区别,但是当Tool被点击以后,地图状态就进行了改变。比如我当前默认对地图的操作是漫游,通过点击一个Tool,接下来在地图上的点击可能就变成了放大操作——与地图交互,这就是Tool的作用。
以下代码定义了一个“使地图处于漫游状态”的Tool:

Tool定义时两个最重要的属性就是clientAction和serverAction。clientAction定义的是在浏览器端执行的操作,比如这里的“EsriMapContinuousPan”代表浏览器端执行的是连续漫游操作——当然,比如你需要在地图里画个多边形之类的操作也可以使用其它的定义,可用的操作大概如表 1所列:
EsriEditingLine 编辑直线 EsriMapLine 画直线
EsriEditingPoint 编辑点 EsriMapMouseWheel 滚轮滚动
EsriEditingPolygon 编辑多边形 EsriMapOval 画椭圆
EsriEditingPolyline 编辑多线 EsriMapPan 漫游
EsriMapCircle 画圆 EsriMapPoint 画点
EsriMapContinuousPan 连续漫游 EsriMapPolygon 画多边形
EsriMapImage 添加图片 EsriMapPolyline 画多线
EsriMapKeyNavigation 键盘导航 EsriMapRectangle 画矩形
表 1 浏览器端支持的操作列表
当用户在浏览器执行了操作以后,ADF会把这个操作相关的信息发送到服务器,然后Tool的serverAction属性中定义的这个类就起作用了。比如上面的这个Tool,服务器接到请求以后会通过PanToolAction的定义对请求过来的参数进行处理(比如向右漫游100个像素),经过一系列计算以后它更新服务器端的WebMap等对象的状态,所有工作完成以后再调用WebContext的refresh方法进行刷新,随后输出响应到浏览器。
serverAction指向的类主要需要实现MapToolAction 接口,在下面的案例中,我们会实现一个自己的ToolAction类。
Tool是另外一种按钮,虽然它在外观上和Command可能没什么区别,但是当Tool被点击以后,地图状态就进行了改变。比如我当前默认对地图的操作是漫游,通过点击一个Tool,接下来在地图上的点击可能就变成了放大操作——与地图交互,这就是Tool的作用。
以下代码定义了一个“使地图处于漫游状态”的Tool:

Tool定义时两个最重要的属性就是clientAction和serverAction。clientAction定义的是在浏览器端执行的操作,比如这里的“EsriMapContinuousPan”代表浏览器端执行的是连续漫游操作——当然,比如你需要在地图里画个多边形之类的操作也可以使用其它的定义,可用的操作大概如表 1所列:
EsriEditingLine 编辑直线 EsriMapLine 画直线
EsriEditingPoint 编辑点 EsriMapMouseWheel 滚轮滚动
EsriEditingPolygon 编辑多边形 EsriMapOval 画椭圆
EsriEditingPolyline 编辑多线 EsriMapPan 漫游
EsriMapCircle 画圆 EsriMapPoint 画点
EsriMapContinuousPan 连续漫游 EsriMapPolygon 画多边形
EsriMapImage 添加图片 EsriMapPolyline 画多线
EsriMapKeyNavigation 键盘导航 EsriMapRectangle 画矩形
表 1 浏览器端支持的操作列表
当用户在浏览器执行了操作以后,ADF会把这个操作相关的信息发送到服务器,然后Tool的serverAction属性中定义的这个类就起作用了。比如上面的这个Tool,服务器接到请求以后会通过PanToolAction的定义对请求过来的参数进行处理(比如向右漫游100个像素),经过一系列计算以后它更新服务器端的WebMap等对象的状态,所有工作完成以后再调用WebContext的refresh方法进行刷新,随后输出响应到浏览器。
serverAction指向的类主要需要实现MapToolAction 接口,在下面的案例中,我们会实现一个自己的ToolAction类。
2009年4月21日星期二
ArcGIS Server Java ADF 案例教程 16
三 WebContext的refresh方法
提示:本小节内容是ADF内部实现机制,对ADF的使用没有影响,如没有兴趣可以跳过。
当调用WebContext的refresh方法的时候, ADF会同步WebContext中所有相关属性。比如当我改变了WebMap的视图范围,然后再调用WebContext的refresh方法,那么ADF会通知WebContext中的WebOverview等对象去同步相关信息,比如同步改变视图范围等。以上过程ADF在后台是通过Observer机制实现的。当ADF调用WebContext的refresh方法时,ADF会通知所有注册到WebContext的WebContextObserver 对象,调用其update方法。实现WebContextObserver这个接口的大概有如下这些类:AWSTocFunctionality, ExtentHistory, MapToolsTask, PrintTask, RoutingTask, SearchAttributesTask, WebMap, WebOverview, WebScaleBar。
以上对WebContext的刷新都是在服务器端。但是,当一个Command被点击以后,服务器端的更新又是怎么被输出到浏览器端的呢?下面就让我们从浏览器端开始,详细地了解一下Command的工作原理。
以下是上一节的案例中用a:command标签在JSF页面中定义了一个Command,在下面我们看一下这段标签在浏览器中相应的输出:

以下是上面的标签在浏览器中的输出:
var zoomIn = new EsriMapServerAction("zoomIn", "");
zoomIn.clientPostBack = true;
zoomIn.defaultImage = "/AgsDemo/images/tasks/maptools/zoomin.png";
zoomIn.hoverImage = "/AgsDemo/images/tasks/maptools/zoominU.png";
zoomIn.selectedImage = "/AgsDemo/images/tasks/maptools/zoominD.png";
zoomIn.isDisabled = false;
zoomIn.showLoading = true;
toolbar.addToolItem(zoomIn);
可见,一个Command对应输出的是EsriMapServerAction这个JavaScript对象,这个对象在“/WebContent/js/esri_map.js”中定义。打开EsriMapServerAction的定义你会发现,当点击这个Command的时候,ADF会提交当前页面;当然,由于我们设置了clientPostBack属性为TRUE,因此交互通过AJAX方式进行,其回调函数为EsriControls的processPostBack函数——EsriControls在“/WebContent/js/esri_core.js”中定义,它包含了所有的ADF客户端组件。
提示:以上这段这里说的很简单,其实包含很多的内容,有兴趣的话可以在这几个JavaScript脚本中仔细研究相关对象和调用关系。
ADF在服务器端会有一个PostBackPhaseListener 去监听所有的AJAX请求,有兴趣的话你可以解开“/WebContent/WEB-INF/lib/arcgis_webcontrols.jar”这个库文件,在其中的“META-INF”目录下有一个“faces-config.xml”中定义了这个PhaseListener。
AJAX请求的参数中会有一个“doPostBack”值,当一个AJAX请求发到服务器的时候,PostBackPhaseListener会监听到这个请求并找到“doPostBack”这个值从而执行后续的操作。当这些操作完成以后,PostBackPhaseListener通过AJAXUtil 的writeResponse方法输出AJAX响应到请求的浏览器。
提示:本小节内容是ADF内部实现机制,对ADF的使用没有影响,如没有兴趣可以跳过。
当调用WebContext的refresh方法的时候, ADF会同步WebContext中所有相关属性。比如当我改变了WebMap的视图范围,然后再调用WebContext的refresh方法,那么ADF会通知WebContext中的WebOverview等对象去同步相关信息,比如同步改变视图范围等。以上过程ADF在后台是通过Observer机制实现的。当ADF调用WebContext的refresh方法时,ADF会通知所有注册到WebContext的WebContextObserver 对象,调用其update方法。实现WebContextObserver这个接口的大概有如下这些类:AWSTocFunctionality, ExtentHistory, MapToolsTask, PrintTask, RoutingTask, SearchAttributesTask, WebMap, WebOverview, WebScaleBar。
以上对WebContext的刷新都是在服务器端。但是,当一个Command被点击以后,服务器端的更新又是怎么被输出到浏览器端的呢?下面就让我们从浏览器端开始,详细地了解一下Command的工作原理。
以下是上一节的案例中用a:command标签在JSF页面中定义了一个Command,在下面我们看一下这段标签在浏览器中相应的输出:

以下是上面的标签在浏览器中的输出:
var zoomIn = new EsriMapServerAction("zoomIn", "");
zoomIn.clientPostBack = true;
zoomIn.defaultImage = "/AgsDemo/images/tasks/maptools/zoomin.png";
zoomIn.hoverImage = "/AgsDemo/images/tasks/maptools/zoominU.png";
zoomIn.selectedImage = "/AgsDemo/images/tasks/maptools/zoominD.png";
zoomIn.isDisabled = false;
zoomIn.showLoading = true;
toolbar.addToolItem(zoomIn);
可见,一个Command对应输出的是EsriMapServerAction这个JavaScript对象,这个对象在“/WebContent/js/esri_map.js”中定义。打开EsriMapServerAction的定义你会发现,当点击这个Command的时候,ADF会提交当前页面;当然,由于我们设置了clientPostBack属性为TRUE,因此交互通过AJAX方式进行,其回调函数为EsriControls的processPostBack函数——EsriControls在“/WebContent/js/esri_core.js”中定义,它包含了所有的ADF客户端组件。
提示:以上这段这里说的很简单,其实包含很多的内容,有兴趣的话可以在这几个JavaScript脚本中仔细研究相关对象和调用关系。
ADF在服务器端会有一个PostBackPhaseListener 去监听所有的AJAX请求,有兴趣的话你可以解开“/WebContent/WEB-INF/lib/arcgis_webcontrols.jar”这个库文件,在其中的“META-INF”目录下有一个“faces-config.xml”中定义了这个PhaseListener。
AJAX请求的参数中会有一个“doPostBack”值,当一个AJAX请求发到服务器的时候,PostBackPhaseListener会监听到这个请求并找到“doPostBack”这个值从而执行后续的操作。当这些操作完成以后,PostBackPhaseListener通过AJAXUtil 的writeResponse方法输出AJAX响应到请求的浏览器。
2009年4月20日星期一
ArcGIS Server Java ADF 案例教程 15
二 【案例】自定义Command使地图等比放大
在这一小节,我们通过自定义Command定制一个按钮,这个按钮每点击一次,地图会放大1倍。通过这个简单案例,让我们来熟悉一下Command的用法。
实现这个功能的主要环节是实现自己的监听器类,以下是这个类的定义:
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.util.WebUtil;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
public class ZoomInListener implements ActionListener
{
public static double ZOOM_FACTOR = 0.5;
public void processAction(ActionEvent event) throws AbortProcessingException
{
WebContext webContext = WebUtil.getWebContext(event.getComponent());
webContext.getWebMap().getCurrentExtent().expand(ZOOM_FACTOR);
webContext.refresh();
}
}
当然,在JSF页面中的用法和上面类似,也很简单:

这个ZoomInListener监听器类主要需要实现了ActionListener的processAction方法,当用户点击这个id为“zoomIn”的Command的时候,这个监听器会被触发,然后其processAction方法就会被执行。
在这个processAction方法中,我们首先获得WebContext对象,通过WebContext我们就可以获得WebMap对象,进而对它的currentExtent属性进行操作(地图范围缩小1倍),再刷新WebContext对象。这个过程很简单,中间有两个环节这里稍作解释。
一个环节是如何获得WebContext对象。processAction是JSF的ActionListener接口的一个方法,这个方法的参数是ActionEvent ,因此它的属性与ADF基本没有关系,所以,获得WebContext就需要用到ADF的WebUtil工具类,ESRI的工程师为了可以在各种环境下获得WebContext这个ADF中非常重要的环境,制造了WebUtil这个工具类及其getWebContext方法,通过这个方法我们可以很方便地获得WebContext对象。
另外一个环节就是WebContext对象的refresh方法,我们在对服务器端的WebMap对象进行一些操作以后,调用了WebContext的refresh方法以后,浏览器中的地图就会刷新成新的视图,其相关的TOC、Overview等组件也会跟着刷新,这很奇妙吧。
在这一小节,我们通过自定义Command定制一个按钮,这个按钮每点击一次,地图会放大1倍。通过这个简单案例,让我们来熟悉一下Command的用法。
实现这个功能的主要环节是实现自己的监听器类,以下是这个类的定义:
import com.esri.adf.web.data.WebContext;
import com.esri.adf.web.util.WebUtil;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
public class ZoomInListener implements ActionListener
{
public static double ZOOM_FACTOR = 0.5;
public void processAction(ActionEvent event) throws AbortProcessingException
{
WebContext webContext = WebUtil.getWebContext(event.getComponent());
webContext.getWebMap().getCurrentExtent().expand(ZOOM_FACTOR);
webContext.refresh();
}
}
当然,在JSF页面中的用法和上面类似,也很简单:

这个ZoomInListener监听器类主要需要实现了ActionListener的processAction方法,当用户点击这个id为“zoomIn”的Command的时候,这个监听器会被触发,然后其processAction方法就会被执行。
在这个processAction方法中,我们首先获得WebContext对象,通过WebContext我们就可以获得WebMap对象,进而对它的currentExtent属性进行操作(地图范围缩小1倍),再刷新WebContext对象。这个过程很简单,中间有两个环节这里稍作解释。
一个环节是如何获得WebContext对象。processAction是JSF的ActionListener接口的一个方法,这个方法的参数是ActionEvent ,因此它的属性与ADF基本没有关系,所以,获得WebContext就需要用到ADF的WebUtil工具类,ESRI的工程师为了可以在各种环境下获得WebContext这个ADF中非常重要的环境,制造了WebUtil这个工具类及其getWebContext方法,通过这个方法我们可以很方便地获得WebContext对象。
另外一个环节就是WebContext对象的refresh方法,我们在对服务器端的WebMap对象进行一些操作以后,调用了WebContext的refresh方法以后,浏览器中的地图就会刷新成新的视图,其相关的TOC、Overview等组件也会跟着刷新,这很奇妙吧。
ArcGIS Server Java ADF 案例教程 14
Command、Tool和Button
这一章我们将讨论的主要对象是Toolbar的孩子们和亲戚,涉及到两个孩子:Command和Tool,还有一个亲戚:Button。它们是基于ADF对WebGIS功能进行扩展的第一步,它可以让你在地图工具条中加入若干自定义的按钮来执行特定的功能。
在第三章中我们把Toolbar列为ADF常用组件之一,但是它和Map、TOC、Overview等其它组件是有区别的,我们打开“context-attributes.xml”配置文件可以发现,Map、TOC、Overview等组件都有相应的JavaBean定义,但是Toolbar没有——因为它不需要在服务器端绑定一个“WebToolbar”对象来记录Toolbar的状态信息。
下面让我们分别来看看这些组件的用法吧。
一 Command
Command在Toolbar中表现为一个按钮,当用户点击这个按钮以后,ADF将会立即执行一些操作。点击Command相当于直接给ADF一个命令,它并不改变当前地图的状态,比如当前地图正在进行漫游,点击Command以后再对地图进行操作时它还是漫游状态。
最典型的Command比如“地图全图”功能,当用户点击以后,当前地图会缩放到全图显示。下面的代码定义了这样一个Command。

从这段代码我们可以看到,Command的最关键的用法在于定义其子元素“f:actionListener”,它的“type”属性指向一个实现了某些功能的监听器类(注意,并不是对象)。这里的“ZoomFullExtentListener”是ADF自带的监听器;当然,这个类也可以是任何实现了ActionListener 接口的类,我们在后面的案例会实现我们自己的监听器类。
下面我们再来看两个稍微有点不同的Command定义。这两个Command的功能是提供两个按钮“后退”、“前进”,当点击“后退”时地图会返回到上一视图范围;当点击“前进”时地图会前进到后一视图范围。下面的代码定义了这样两个Command。

这里并没有使用监听器,而是使用了Command的action属性。这个属性将绑定一个服务器端对象的方法,当点击这个Command的时候,ADF会去调用这个服务器端方法。“后退”、“前进”的功能主要使用了ADF中的ExtentHistory 对象,它可以维护地图的视图范围历史记录。下面它是在“context-attributes.xml”中的定义:

当我们点击“后退”按钮的时候,ADF会调用这个服务器端“history”对象的doPrevious方法,因此地图会切换到上一视图;点击“前进”也是类似。另外,这两个Command的disabled属性分别绑定到“history”对象的canUndo和canRedo属性,这也很好理解,当地图已经是最新的视图的时候,用户当然应该不可以点击“前进”这个按钮,此时“history”的canRedo属性值为TRUE,因此“前进”这个按钮会呈现不可用的状态。
稍微总结一下,上面主要介绍了Command的两种定义用法,一种定义监听器类作为Command的子元素;另外一种绑定服务器端方法到Command的action属性。对于自定义Command来说,一般使用监听器会比较容易一些。
这一章我们将讨论的主要对象是Toolbar的孩子们和亲戚,涉及到两个孩子:Command和Tool,还有一个亲戚:Button。它们是基于ADF对WebGIS功能进行扩展的第一步,它可以让你在地图工具条中加入若干自定义的按钮来执行特定的功能。
在第三章中我们把Toolbar列为ADF常用组件之一,但是它和Map、TOC、Overview等其它组件是有区别的,我们打开“context-attributes.xml”配置文件可以发现,Map、TOC、Overview等组件都有相应的JavaBean定义,但是Toolbar没有——因为它不需要在服务器端绑定一个“WebToolbar”对象来记录Toolbar的状态信息。
下面让我们分别来看看这些组件的用法吧。
一 Command
Command在Toolbar中表现为一个按钮,当用户点击这个按钮以后,ADF将会立即执行一些操作。点击Command相当于直接给ADF一个命令,它并不改变当前地图的状态,比如当前地图正在进行漫游,点击Command以后再对地图进行操作时它还是漫游状态。
最典型的Command比如“地图全图”功能,当用户点击以后,当前地图会缩放到全图显示。下面的代码定义了这样一个Command。

从这段代码我们可以看到,Command的最关键的用法在于定义其子元素“f:actionListener”,它的“type”属性指向一个实现了某些功能的监听器类(注意,并不是对象)。这里的“ZoomFullExtentListener”是ADF自带的监听器;当然,这个类也可以是任何实现了ActionListener 接口的类,我们在后面的案例会实现我们自己的监听器类。
下面我们再来看两个稍微有点不同的Command定义。这两个Command的功能是提供两个按钮“后退”、“前进”,当点击“后退”时地图会返回到上一视图范围;当点击“前进”时地图会前进到后一视图范围。下面的代码定义了这样两个Command。

这里并没有使用监听器,而是使用了Command的action属性。这个属性将绑定一个服务器端对象的方法,当点击这个Command的时候,ADF会去调用这个服务器端方法。“后退”、“前进”的功能主要使用了ADF中的ExtentHistory 对象,它可以维护地图的视图范围历史记录。下面它是在“context-attributes.xml”中的定义:

当我们点击“后退”按钮的时候,ADF会调用这个服务器端“history”对象的doPrevious方法,因此地图会切换到上一视图;点击“前进”也是类似。另外,这两个Command的disabled属性分别绑定到“history”对象的canUndo和canRedo属性,这也很好理解,当地图已经是最新的视图的时候,用户当然应该不可以点击“前进”这个按钮,此时“history”的canRedo属性值为TRUE,因此“前进”这个按钮会呈现不可用的状态。
稍微总结一下,上面主要介绍了Command的两种定义用法,一种定义监听器类作为Command的子元素;另外一种绑定服务器端方法到Command的action属性。对于自定义Command来说,一般使用监听器会比较容易一些。
2009年4月17日星期五
B/S架构中的数据推送技术
B/S架构中服务器向浏览器推送数据在很多场合都有需求,比如实时的监控报警、实时的调度,等等。凡是对实时性要求越高的场景,越是需要服务器及时、准确地向浏览器推送数据。这里我们就讨论一下在B/S架构下,可以实现从服务器向浏览器推送数据的几种技术及其相应的特点。
基于HTTP协议
1. HTTP协议的特点
纯的HTTP协议在本质上是无状态、无连接的,它基于请求/响应的工作模式,使得浏览器在每次发生请求的时候和服务器建立连接,当接收到响应的时候断开连接;在这种情况下,要让服务器主动向浏览器发送数据是不可能的。
技术总是为需求服务的,在很多浏览器需要及时获取服务器数据更新的需要下,技术人员们变通地发明了一些基于HTTP协议的“伪”长连接技术,实现了服务器数据向浏览器的“准”推送。
2. 定时刷新
定时刷新是最粗糙的实现快速感知服务器数据变化的方法,原理就是不停地刷新页面从而显示最新的服务器端数据。定时刷新从技术上大体有两种实现方法:一是通过HTML的META标签设置页面刷新间隔,比如以下的标签会在浏览器中每隔10秒刷新当前页面:
<meta equiv="refresh" content="10" />
这种方式会看到页面有明显的刷新,用户体验会比较差;与此相对应的另外一种方式就是AJAX,在JavaScript脚本中添加一个定时器,每隔一定时间向服务器发送一个AJAX请求,得到响应以后去更新页面的内容。以下JavaScript代码会每隔10秒发送一个AJAX请求去更新当前页面的部分内容:
setInterval(function(){sendAJAXRequest()},10000);
如图 1是定时刷新的模型图:
定时刷新这种模式相当于一个下级每隔一段时间打电话给上级,请示当前指示。当然,如果在这个时间段内发生了一些情况,他们之间是没办法沟通的,所以信息并不是“实时”的;另外,刷新间隔到底设置多少合适也是个问题,服务器的负担也比较大。
3. 轮询
轮询也是通过AJAX进行的,它与AJAX定时刷新的区别仅在于接收到AJAX响应以后的工作。定时刷新完全是浏览器主动的,在浏览器和服务器之间存在一定的断开间隔;而轮询这种方式会在AJAX响应中再次发送AJAX请求,也就是说当响应结束,浏览器和服务器之间一旦断开连接的时候,浏览器会再次发送请求建立连接。
如图 2是轮询的模型图:
轮询这种模式相当于一个下级不停给上级打电话请示,上级指示下达以后挂了电话,下级可能去执行指示或者不执行,但是随即下级将再次给上级打电话继续请示。这种模式可以保证数据更新比较实时,但是服务器负担也是一个问题。
顺便提一下,轮询这种模式一般会在浏览器的XMLHttpRequest的readyState为4(响应数据传输结束)的时候调用回调函数,此时连接已经关闭;但是在Firefox中支持Streaming AJAX模式,也就是XMLHttpRequest的readyState为3(响应数据仍在传输)的时候调用回调函数,此时连接尚未关闭。
4. 基于iframe的流模式
iframe可以在当前页面中嵌入一个文档页面,如果把这个页面设为隐藏,并将其src属性设置为一个特殊的请求,数据将会源源不断地从服务器发送到浏览器。
比如以下的JSP页面,它将每隔10秒更新一个数输出到浏览器,不出意外它将会永远执行下去,浏览器和服务器之间的连接也永远不会关闭:
<% int i = 1; try { while (true) { out.print("
out.flush();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
}
} catch (Exception e) {}
%>
但是这样做有个很显然的不足,由于响应始终没有结束,因此浏览器里面的加载进度条会始终显示加载没有完成。当然,这也并不是没有办法解决,比如http://www.zeitoun.net/articles/comet_and_php/start就提供了IE、Opera、WebKit核心(Chrome/Safari)、Gecko核心(Firefox)等浏览器的解决方案。
如图 3是基于iframe流模式的模型图:
基于iframe的流模式相当于一个下级给上级打电话,上级不停发出指示,下级一边接收指示、一边执行。这种模式有着比较好的实时性,比如Gmail就是采用这种模式。
5. 开源框架Pushlets
Pushlets是一个实现了AJAX轮询和iframe流模式的开源框架(Java+JavaScript),对此有兴趣的可以参考:http://www.pushlets.com/,Pushlets采用LGPL许可。
基于消息
1. 基于消息的架构
使用消息可以实现松散耦合的分布式数据通讯。通过消息从服务器向浏览器推送数据一般需要一个消息中间件(Message Oriented Middleware,MOM),服务器将数据推送给消息中间件,消息中间件再将数据以消息的方式推送给浏览器。基于消息的架构有个特别大的优点,那就是不但可以实现服务器向单浏览器、服务器向多浏览器推送数据;还支持浏览器到浏览器之间的数据推送。这将会在预警、调度等场合有非常大的用武之地。
图 4和图 5分别是基于订阅/发布模式和点到点模式的消息传递示意。在B/S架构中,服务器可以发布消息,所有订阅该主题的浏览器都会接受到该消息,这就实现了从服务器向多浏览器的数据推送;浏览器或者服务器也可以向特定的对象发送消息,消息将在一个消息队列中被发送,对方浏览器就可以收到该消息,这就实现了服务器向某特定浏览器或者浏览器之间的数据推送。

2. Java消息服务(JMS)
JMS是一组公开的Java API,它定义了与消息相关的接口和语义,目前JMS已经成为J2EE中的重要组成部分。
如图 6是JMS工作原理。当有客户端连接到JMS服务器的时候,JMS的连接工厂会根据连接类型来创建一个虚拟连接,这个连接会具体负责消息的传递;在这个连接建立完成后,会产生一个会话,会话中保存了消息生产者或消息消费者的信息;消息的消费者会对感兴趣的消息目的地(队列或主题)建立一个监听,消息的生产者则负责把消息发送到这个目的地上。
另外JMS还有一些值得一提的特点,比如支持事务性会话、可以设置消息的持久性、设置消息的优先级、允许消息过期、可以构建长期订阅等。这些特性在各种企业级应用环境下都有可能提高应用的功能或性能。
实现JMS的商业中间件有IBM MQSeries、BEA WebLogic JMS等;开源中间件有OpenJMS、Apache ActiveMQ等。
3. ActiveMQ
ActiveMQ是Apache基金会的著名开源项目,目前Release版本5.2完整支持JMS1.1和J2EE 1.4规范。ActiveMQ采用Apache 2.0许可发布。ActiveMQ的优势在于其支持集群部署、支持多种应用层协议和诸多客户端开发语言等特点。
ActiveMQ主要支持以下协议:
OpenWire
REST
Stomp
WS Notification
XMPP
AMQP
下面这里将使用ActiveMQ和Stomp协议来演示各种方式的数据的推送。
4. Stomp协议
Stomp是一种简单、实现容易的协议,因此支持非常广泛,这里采用Stomp协议的主要原因也是因为其支持的客户端开发语言最多,在各种环境下都有用武之地。这些开发语言主要包括:
ActionScript 3
C
C++
.Net
Delphi
Perl
PHP
Python
Ruby
下面主要用到了ActionScript和.Net语言。
5. 发布/订阅模式的实现
发布/订阅模式适用于广播性质的数据推送。比如在实时监控系统中,当我们的指令中心需要向所有监控目标发送信息的时候,这种模式就比较适合。为了简单起见,这里的服务器端和浏览器端都使用了ActionScript实现。
在浏览器端,我们需要订阅服务器主题。比如所有的接收指令的目标都需要监听“Alarm”频道,那么在浏览器中的代码应该如下:
private function sub():void
{
var ch:ConnectHeaders = new ConnectHeaders();
stomp.connect("localhost", 61613, ch);
stomp.subscribe( "/topic/Alarm" );
}
private function onStompMessage(event:MessageEvent):void
{
var byteArray:ByteArray = event.message.body;
var str:String = byteArray.toString();
}
6. 点对点模式的实现
点对点模式适合类似调度的功能场景。比如在监控系统中,当某个指令需要下达给具体某个目标的时候,就需要点对点模式的数据推送。
实现点对点模式的数据推送需要知道数据发送的目的地,以下代码演示了如何从指令中心“党中央”发送指令到“毒蛇”的过程:
private function sendMsg():void
{
var destination:String = "/queue/毒蛇";
stomp.sendTextMessage(destination, “注意隐蔽”);
}
当然,“毒蛇”需要监听所有发送给他的消息:
private function login():void
{
var ch:ConnectHeaders = new ConnectHeaders();
stomp.connect("localhost", 61613, ch);
stomp.subscribe( "/queue/毒蛇" );
}
7. 实时监控Demo
这里通过消息模式实现了一个实时监控的演示。服务器端是使用.Net Stomp API写的控制台程序,Demo测试使用1000个监控目标,数据发送间隔0.5秒,数据推送到“realmonitor”频道。浏览器端使用Flex,监听“realmonitor”频道。
在服务器端,这个Demo中设置每100个目标信息拼装成一条消息,也就是说每0.5秒会发送10条消息。如果每条消息的信息(坐标和一些属性信息)大约50字节的话,每条消息大概5KB;每秒发送两次,总共大概100KB数据量会被推送。
如图 9是在Flex中实现的实时监控效果。
基于HTTP协议
1. HTTP协议的特点
纯的HTTP协议在本质上是无状态、无连接的,它基于请求/响应的工作模式,使得浏览器在每次发生请求的时候和服务器建立连接,当接收到响应的时候断开连接;在这种情况下,要让服务器主动向浏览器发送数据是不可能的。
技术总是为需求服务的,在很多浏览器需要及时获取服务器数据更新的需要下,技术人员们变通地发明了一些基于HTTP协议的“伪”长连接技术,实现了服务器数据向浏览器的“准”推送。
2. 定时刷新
定时刷新是最粗糙的实现快速感知服务器数据变化的方法,原理就是不停地刷新页面从而显示最新的服务器端数据。定时刷新从技术上大体有两种实现方法:一是通过HTML的META标签设置页面刷新间隔,比如以下的标签会在浏览器中每隔10秒刷新当前页面:
<meta equiv="refresh" content="10" />
这种方式会看到页面有明显的刷新,用户体验会比较差;与此相对应的另外一种方式就是AJAX,在JavaScript脚本中添加一个定时器,每隔一定时间向服务器发送一个AJAX请求,得到响应以后去更新页面的内容。以下JavaScript代码会每隔10秒发送一个AJAX请求去更新当前页面的部分内容:
setInterval(function(){sendAJAXRequest()},10000);
如图 1是定时刷新的模型图:
定时刷新这种模式相当于一个下级每隔一段时间打电话给上级,请示当前指示。当然,如果在这个时间段内发生了一些情况,他们之间是没办法沟通的,所以信息并不是“实时”的;另外,刷新间隔到底设置多少合适也是个问题,服务器的负担也比较大。
3. 轮询
轮询也是通过AJAX进行的,它与AJAX定时刷新的区别仅在于接收到AJAX响应以后的工作。定时刷新完全是浏览器主动的,在浏览器和服务器之间存在一定的断开间隔;而轮询这种方式会在AJAX响应中再次发送AJAX请求,也就是说当响应结束,浏览器和服务器之间一旦断开连接的时候,浏览器会再次发送请求建立连接。
如图 2是轮询的模型图:
轮询这种模式相当于一个下级不停给上级打电话请示,上级指示下达以后挂了电话,下级可能去执行指示或者不执行,但是随即下级将再次给上级打电话继续请示。这种模式可以保证数据更新比较实时,但是服务器负担也是一个问题。
顺便提一下,轮询这种模式一般会在浏览器的XMLHttpRequest的readyState为4(响应数据传输结束)的时候调用回调函数,此时连接已经关闭;但是在Firefox中支持Streaming AJAX模式,也就是XMLHttpRequest的readyState为3(响应数据仍在传输)的时候调用回调函数,此时连接尚未关闭。
4. 基于iframe的流模式
iframe可以在当前页面中嵌入一个文档页面,如果把这个页面设为隐藏,并将其src属性设置为一个特殊的请求,数据将会源源不断地从服务器发送到浏览器。
比如以下的JSP页面,它将每隔10秒更新一个数输出到浏览器,不出意外它将会永远执行下去,浏览器和服务器之间的连接也永远不会关闭:
<% int i = 1; try { while (true) { out.print("
"+(i++)+"
");out.flush();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
}
} catch (Exception e) {}
%>
但是这样做有个很显然的不足,由于响应始终没有结束,因此浏览器里面的加载进度条会始终显示加载没有完成。当然,这也并不是没有办法解决,比如http://www.zeitoun.net/articles/comet_and_php/start就提供了IE、Opera、WebKit核心(Chrome/Safari)、Gecko核心(Firefox)等浏览器的解决方案。
如图 3是基于iframe流模式的模型图:
基于iframe的流模式相当于一个下级给上级打电话,上级不停发出指示,下级一边接收指示、一边执行。这种模式有着比较好的实时性,比如Gmail就是采用这种模式。
5. 开源框架Pushlets
Pushlets是一个实现了AJAX轮询和iframe流模式的开源框架(Java+JavaScript),对此有兴趣的可以参考:http://www.pushlets.com/,Pushlets采用LGPL许可。
基于消息
1. 基于消息的架构
使用消息可以实现松散耦合的分布式数据通讯。通过消息从服务器向浏览器推送数据一般需要一个消息中间件(Message Oriented Middleware,MOM),服务器将数据推送给消息中间件,消息中间件再将数据以消息的方式推送给浏览器。基于消息的架构有个特别大的优点,那就是不但可以实现服务器向单浏览器、服务器向多浏览器推送数据;还支持浏览器到浏览器之间的数据推送。这将会在预警、调度等场合有非常大的用武之地。
图 4和图 5分别是基于订阅/发布模式和点到点模式的消息传递示意。在B/S架构中,服务器可以发布消息,所有订阅该主题的浏览器都会接受到该消息,这就实现了从服务器向多浏览器的数据推送;浏览器或者服务器也可以向特定的对象发送消息,消息将在一个消息队列中被发送,对方浏览器就可以收到该消息,这就实现了服务器向某特定浏览器或者浏览器之间的数据推送。

2. Java消息服务(JMS)
JMS是一组公开的Java API,它定义了与消息相关的接口和语义,目前JMS已经成为J2EE中的重要组成部分。
如图 6是JMS工作原理。当有客户端连接到JMS服务器的时候,JMS的连接工厂会根据连接类型来创建一个虚拟连接,这个连接会具体负责消息的传递;在这个连接建立完成后,会产生一个会话,会话中保存了消息生产者或消息消费者的信息;消息的消费者会对感兴趣的消息目的地(队列或主题)建立一个监听,消息的生产者则负责把消息发送到这个目的地上。
另外JMS还有一些值得一提的特点,比如支持事务性会话、可以设置消息的持久性、设置消息的优先级、允许消息过期、可以构建长期订阅等。这些特性在各种企业级应用环境下都有可能提高应用的功能或性能。
实现JMS的商业中间件有IBM MQSeries、BEA WebLogic JMS等;开源中间件有OpenJMS、Apache ActiveMQ等。
3. ActiveMQ
ActiveMQ是Apache基金会的著名开源项目,目前Release版本5.2完整支持JMS1.1和J2EE 1.4规范。ActiveMQ采用Apache 2.0许可发布。ActiveMQ的优势在于其支持集群部署、支持多种应用层协议和诸多客户端开发语言等特点。
ActiveMQ主要支持以下协议:
OpenWire
REST
Stomp
WS Notification
XMPP
AMQP
下面这里将使用ActiveMQ和Stomp协议来演示各种方式的数据的推送。
4. Stomp协议
Stomp是一种简单、实现容易的协议,因此支持非常广泛,这里采用Stomp协议的主要原因也是因为其支持的客户端开发语言最多,在各种环境下都有用武之地。这些开发语言主要包括:
ActionScript 3
C
C++
.Net
Delphi
Perl
PHP
Python
Ruby
下面主要用到了ActionScript和.Net语言。
5. 发布/订阅模式的实现
发布/订阅模式适用于广播性质的数据推送。比如在实时监控系统中,当我们的指令中心需要向所有监控目标发送信息的时候,这种模式就比较适合。为了简单起见,这里的服务器端和浏览器端都使用了ActionScript实现。
在浏览器端,我们需要订阅服务器主题。比如所有的接收指令的目标都需要监听“Alarm”频道,那么在浏览器中的代码应该如下:
private function sub():void
{
var ch:ConnectHeaders = new ConnectHeaders();
stomp.connect("localhost", 61613, ch);
stomp.subscribe( "/topic/Alarm" );
}
private function onStompMessage(event:MessageEvent):void
{
var byteArray:ByteArray = event.message.body;
var str:String = byteArray.toString();
}
6. 点对点模式的实现
点对点模式适合类似调度的功能场景。比如在监控系统中,当某个指令需要下达给具体某个目标的时候,就需要点对点模式的数据推送。
实现点对点模式的数据推送需要知道数据发送的目的地,以下代码演示了如何从指令中心“党中央”发送指令到“毒蛇”的过程:
private function sendMsg():void
{
var destination:String = "/queue/毒蛇";
stomp.sendTextMessage(destination, “注意隐蔽”);
}
当然,“毒蛇”需要监听所有发送给他的消息:
private function login():void
{
var ch:ConnectHeaders = new ConnectHeaders();
stomp.connect("localhost", 61613, ch);
stomp.subscribe( "/queue/毒蛇" );
}
7. 实时监控Demo
这里通过消息模式实现了一个实时监控的演示。服务器端是使用.Net Stomp API写的控制台程序,Demo测试使用1000个监控目标,数据发送间隔0.5秒,数据推送到“realmonitor”频道。浏览器端使用Flex,监听“realmonitor”频道。
在服务器端,这个Demo中设置每100个目标信息拼装成一条消息,也就是说每0.5秒会发送10条消息。如果每条消息的信息(坐标和一些属性信息)大约50字节的话,每条消息大概5KB;每秒发送两次,总共大概100KB数据量会被推送。
如图 9是在Flex中实现的实时监控效果。
2009年4月15日星期三
ArcGIS Server Java ADF 案例教程 13
【案例】给地图加上弹出气泡
在这一小节里,我们将会在地图上加一个气泡,很多时候你需要弹出一些简短的信息到地图上,气泡就是为了满足这样的要求的。
首先让我们回忆下这一章的主要内容,前面讲了ADF一些常用的地图组件,然后从Map组件入手详细分析了它在服务器端和浏览器端的具体实现。现在我们的气泡功能主要是对浏览器端的Map进行功能扩展,那么首先应该想到的就是EsriMap这个JavaScript类,我们在EsriMap中弹出一个div,再放上我需要的信息不就可以了么?
等等,在页面上弹出一个div是很简单,但是用户还会操作地图,比如拖动一下啊什么的,那这个div怎么跟地图一起移动呢?再回想下图 9,你应该可以得到答案吧?对了,把这个div放到imageGrid中去。
下面让我们来具体实现这个功能,先看一下效果吧,如图 10。
在这个简单的页面上,我们放置了两个按钮。当点击“显示气泡”按钮的时候,地图上会弹出一个提示框,提示框中有我们想要显示给用户的信息;当点击“隐藏气泡”按钮的时候,这个提示框会隐藏掉;另外,当我们拖动地图的时候,这个气泡也会跟着地图一起移动。
在这里主要封装了两个JavaScript函数,分别绑定到“显示气泡”和“隐藏气泡”按钮的onclick事件上。
我们先来看一下“显示气泡”按钮的事件,它将会触发showPopUp函数,这个函数执行的操作如下:
function showPopUp()
{
var popUpId = 'divPopUp';
var popUp = document.getElementById(popUpId);
if( popUp==null )
{
popUp = document.createElement('div');
popUp.id = popUpId;
popUp.innerHTML = "这是我要弹出的信息";
var map = EsriControls.maps['map'];
map.imageGrid.appendChild(popUp);
}
var style = 'display:block;position:absolute;z-index:2;';
style += 'left:100; top:100;';
style += 'background-image:url(images/wuyf/popup.png);width:400px; height:200px;';
style += 'background-repeat:no-repeat;';
style += 'padding:10px;';
EsriUtils.setElementStyle(popUp, style);
}
首先,我们试图通过getElementById()方法去获得气泡div是否已经存在;如果不存在,则新建这个div并添加到地图上。我们看到,把气泡div(id为divPopUp)添加到地图上的过程实际上就是把这个div添加到EsriMap的imageGrid中去,这主要是通过appendChild(popUp)这个方法实现的。
仅仅把这个div添加到imageGrid以后并不能看到这个它,在下面还有一些其它的设置,否则我们的气泡div会被地图遮挡住。这里主要的概念是CSS中的z-index属性,使用这个属性来控制imageGrid中各个子元素的叠置顺序,imageGrid中地图图片的z-index值为1,因此我们需要给气泡div设置一个比1大的数值才可以。另外,在气泡div的样式中我们还必须通过“display:block;”来设置这个div可见,再通过“position:absolute;”来设置这个div的定位模式,这些都是必须的。
顺便提一句,这里使用的是EsriUtils的setElementStyle()方法来给气泡div来设置CSS属性;你直接用obj.style.attributes=value;的方法来设置也是一样的。
再来看一下“隐藏气泡”按钮的事件,这就简单多了:
function hidePopUp()
{
var popUpId = 'divPopUp';
var popUp = document.getElementById(popUpId);
if( popUp!=null )
{
EsriUtils.setElementStyle(popUp, "display:none;");
}
}
这里事实上只做了一件事情,就是把气泡div的display样式设置成“none”,浏览器就不会去渲染这个气泡了。
通过这个小案例,你是不是对ADF中的Map组件,特别是JavaScript脚本库中的EsriMap有了更加深入的理解了?
在这一小节里,我们将会在地图上加一个气泡,很多时候你需要弹出一些简短的信息到地图上,气泡就是为了满足这样的要求的。
首先让我们回忆下这一章的主要内容,前面讲了ADF一些常用的地图组件,然后从Map组件入手详细分析了它在服务器端和浏览器端的具体实现。现在我们的气泡功能主要是对浏览器端的Map进行功能扩展,那么首先应该想到的就是EsriMap这个JavaScript类,我们在EsriMap中弹出一个div,再放上我需要的信息不就可以了么?
等等,在页面上弹出一个div是很简单,但是用户还会操作地图,比如拖动一下啊什么的,那这个div怎么跟地图一起移动呢?再回想下图 9,你应该可以得到答案吧?对了,把这个div放到imageGrid中去。
下面让我们来具体实现这个功能,先看一下效果吧,如图 10。
在这个简单的页面上,我们放置了两个按钮。当点击“显示气泡”按钮的时候,地图上会弹出一个提示框,提示框中有我们想要显示给用户的信息;当点击“隐藏气泡”按钮的时候,这个提示框会隐藏掉;另外,当我们拖动地图的时候,这个气泡也会跟着地图一起移动。
在这里主要封装了两个JavaScript函数,分别绑定到“显示气泡”和“隐藏气泡”按钮的onclick事件上。
我们先来看一下“显示气泡”按钮的事件,它将会触发showPopUp函数,这个函数执行的操作如下:
function showPopUp()
{
var popUpId = 'divPopUp';
var popUp = document.getElementById(popUpId);
if( popUp==null )
{
popUp = document.createElement('div');
popUp.id = popUpId;
popUp.innerHTML = "这是我要弹出的信息";
var map = EsriControls.maps['map'];
map.imageGrid.appendChild(popUp);
}
var style = 'display:block;position:absolute;z-index:2;';
style += 'left:100; top:100;';
style += 'background-image:url(images/wuyf/popup.png);width:400px; height:200px;';
style += 'background-repeat:no-repeat;';
style += 'padding:10px;';
EsriUtils.setElementStyle(popUp, style);
}
首先,我们试图通过getElementById()方法去获得气泡div是否已经存在;如果不存在,则新建这个div并添加到地图上。我们看到,把气泡div(id为divPopUp)添加到地图上的过程实际上就是把这个div添加到EsriMap的imageGrid中去,这主要是通过appendChild(popUp)这个方法实现的。
仅仅把这个div添加到imageGrid以后并不能看到这个它,在下面还有一些其它的设置,否则我们的气泡div会被地图遮挡住。这里主要的概念是CSS中的z-index属性,使用这个属性来控制imageGrid中各个子元素的叠置顺序,imageGrid中地图图片的z-index值为1,因此我们需要给气泡div设置一个比1大的数值才可以。另外,在气泡div的样式中我们还必须通过“display:block;”来设置这个div可见,再通过“position:absolute;”来设置这个div的定位模式,这些都是必须的。
顺便提一句,这里使用的是EsriUtils的setElementStyle()方法来给气泡div来设置CSS属性;你直接用obj.style.attributes=value;的方法来设置也是一样的。
再来看一下“隐藏气泡”按钮的事件,这就简单多了:
function hidePopUp()
{
var popUpId = 'divPopUp';
var popUp = document.getElementById(popUpId);
if( popUp!=null )
{
EsriUtils.setElementStyle(popUp, "display:none;");
}
}
这里事实上只做了一件事情,就是把气泡div的display样式设置成“none”,浏览器就不会去渲染这个气泡了。
通过这个小案例,你是不是对ADF中的Map组件,特别是JavaScript脚本库中的EsriMap有了更加深入的理解了?
2009年4月8日星期三
ArcGIS Server Java ADF 案例教程 12
EsriMap详解
提示:本小节所涉及内容主要由ADF自己完成,分析只是为了更好地了解原理,为将来有可能做的扩展进行准备。
EsriMap在“/WebContent/js/esri_map.js”中定义,让我们从Map组件输出的JavaScript代码开始去了解ADF在浏览器中所做的工作。
下面的代码是一个简单地图输出的JavaScript代码(为了便于阅读,我对某些代码作了等价的修改),其中“AgsTest”是我的项目名称:




现在让我们开始看浏览器一步一步执行的JavaScript代码。除了引入一些ADF的JavaScript脚本,首先将会执行的是位于最上面的一些变量声明和位于最下面的esriInitItems.push("esriMapInitmap()")代码,这里会将esriMapInitmap()这个函数添加到esriInitItems这个初始化数组里面去;然后,在页面加载完成后会触发window.onload事件,以致调用esriInitApp(e)函数;这时,由于esriMapInitmap()已经被添加到esriInitItems中,因此这个函数将被调用。下面让我们仔细看看在esriMapInitmap()中发生了些什么。
首先第一件事情就是实例化一个EsriMap对象,这一步其实有很多内涵,马上就会详细解释;其次,ADF会针对地图中最重要的数据源进行配置,我们在上面的代码可以看到,由于我们使用的是动态Map Service,因此ADF会新建一个EsriMapSourceDynamic(继承自EsriMapSource)对象,并通过EsriMap的addMapSource()方法添加到地图中;然后,addImage()方法会添加这个EsriMapSource对象的图片地址,可以看到,这个图片地址是一个相对路径,以下这个地址表明EsriMapSource中使用的图片格式是MIME 数据,jsessionid参数指示了当前会话的id:
/AgsTest/mimedata;jsessionid=5E13F6727CAA0433934A85EFA1AB8D49?wname=esriWebSession&id=map28171097&uniqueId=24691856-0
下面回过头去详细看一下在实例化EsriMap的时候所发生的事情,这一切相关的操作都封装在了“esri_map.js”这个文件中了,我们可以打开这个文件来看一下EsriMap实例化的详细内容。
首先,EsriMap是继承自EsriControl,关于EsriControl及其相关的内容,有兴趣可以在ADF JavaScript库中继续深入研究。事实上我们对EsriMap感兴趣的应该主要是两点:其一,地图图片在EsriMap中的放置关系;其二,对地图做操作时候EsriMap与ArcGIS Server的通讯。
我们先来看下EsriMap中地图图片的放置关系。EsriMap最外层的div是container,默认id是“EsriMapCell_map”(在“map.xsl”中定义),它是放置地图的容器,一般不作操作;在container中还有几个层次的div:divObject、controlDiv、imageGrid,它们和container的关系如图 9所示。地图图片会放在imageGrid中。
下面我们以动态地图为例,看一下当拖动地图时EsriMap是如何与ArcGIS Server通讯并更新图片的。我们首先找到EsriMap中有以下这段代码:
EsriControls.addPostBackTagHandler("map", EsriControls.maps[self.id].updateAsync);
EsriControls在“/WebContent/js/esri_core.js”中定义,它负责管理当前页面上所有的ADF的DHTML组件。以上的代码给EsriMap对象添加了一个回调函数updateAsync,它将会在页面收到AJAX响应并且响应中有“map”标签的时候被调用。
当我们拖动地图的时候,客户端的JavaScript会将需要的地图范围发给服务器(有兴趣可以参考EsriMapPan和EsriMapContinuousPan函数),服务器处理完成后会返回如下的响应:


这时,updateAsync函数就会被调用,它会更新页面上EsriMap相关的一系列div和其它对象的属性值,从而更新地图视图。具体更新的细节,有兴趣可以详细研究下updateAsync函数,这里就不赘述了。
从这个Map组件的服务器端对象和客户端对象,我们已经基本了解了Map的工作原理。ADF的其余几个常用组件:TOC、Toolbar、Overview等也是万变不离其宗,虽各有特点,但大体思路和原理也大概如此,有兴趣的话你还可以做更深入的研究。
提示:本小节所涉及内容主要由ADF自己完成,分析只是为了更好地了解原理,为将来有可能做的扩展进行准备。
EsriMap在“/WebContent/js/esri_map.js”中定义,让我们从Map组件输出的JavaScript代码开始去了解ADF在浏览器中所做的工作。
下面的代码是一个简单地图输出的JavaScript代码(为了便于阅读,我对某些代码作了等价的修改),其中“AgsTest”是我的项目名称:




现在让我们开始看浏览器一步一步执行的JavaScript代码。除了引入一些ADF的JavaScript脚本,首先将会执行的是位于最上面的一些变量声明和位于最下面的esriInitItems.push("esriMapInitmap()")代码,这里会将esriMapInitmap()这个函数添加到esriInitItems这个初始化数组里面去;然后,在页面加载完成后会触发window.onload事件,以致调用esriInitApp(e)函数;这时,由于esriMapInitmap()已经被添加到esriInitItems中,因此这个函数将被调用。下面让我们仔细看看在esriMapInitmap()中发生了些什么。
首先第一件事情就是实例化一个EsriMap对象,这一步其实有很多内涵,马上就会详细解释;其次,ADF会针对地图中最重要的数据源进行配置,我们在上面的代码可以看到,由于我们使用的是动态Map Service,因此ADF会新建一个EsriMapSourceDynamic(继承自EsriMapSource)对象,并通过EsriMap的addMapSource()方法添加到地图中;然后,addImage()方法会添加这个EsriMapSource对象的图片地址,可以看到,这个图片地址是一个相对路径,以下这个地址表明EsriMapSource中使用的图片格式是MIME 数据,jsessionid参数指示了当前会话的id:
/AgsTest/mimedata;jsessionid=5E13F6727CAA0433934A85EFA1AB8D49?wname=esriWebSession&id=map28171097&uniqueId=24691856-0
下面回过头去详细看一下在实例化EsriMap的时候所发生的事情,这一切相关的操作都封装在了“esri_map.js”这个文件中了,我们可以打开这个文件来看一下EsriMap实例化的详细内容。
首先,EsriMap是继承自EsriControl,关于EsriControl及其相关的内容,有兴趣可以在ADF JavaScript库中继续深入研究。事实上我们对EsriMap感兴趣的应该主要是两点:其一,地图图片在EsriMap中的放置关系;其二,对地图做操作时候EsriMap与ArcGIS Server的通讯。
我们先来看下EsriMap中地图图片的放置关系。EsriMap最外层的div是container,默认id是“EsriMapCell_map”(在“map.xsl”中定义),它是放置地图的容器,一般不作操作;在container中还有几个层次的div:divObject、controlDiv、imageGrid,它们和container的关系如图 9所示。地图图片会放在imageGrid中。
下面我们以动态地图为例,看一下当拖动地图时EsriMap是如何与ArcGIS Server通讯并更新图片的。我们首先找到EsriMap中有以下这段代码:
EsriControls.addPostBackTagHandler("map", EsriControls.maps[self.id].updateAsync);
EsriControls在“/WebContent/js/esri_core.js”中定义,它负责管理当前页面上所有的ADF的DHTML组件。以上的代码给EsriMap对象添加了一个回调函数updateAsync,它将会在页面收到AJAX响应并且响应中有“map”标签的时候被调用。
当我们拖动地图的时候,客户端的JavaScript会将需要的地图范围发给服务器(有兴趣可以参考EsriMapPan和EsriMapContinuousPan函数),服务器处理完成后会返回如下的响应:


这时,updateAsync函数就会被调用,它会更新页面上EsriMap相关的一系列div和其它对象的属性值,从而更新地图视图。具体更新的细节,有兴趣可以详细研究下updateAsync函数,这里就不赘述了。
从这个Map组件的服务器端对象和客户端对象,我们已经基本了解了Map的工作原理。ADF的其余几个常用组件:TOC、Toolbar、Overview等也是万变不离其宗,虽各有特点,但大体思路和原理也大概如此,有兴趣的话你还可以做更深入的研究。
ArcGIS Server Java ADF 案例教程 11
Map组件详解
以上几个组件的使用非常简单,不需要很多设置就可以搭起一个有模有样的WebGIS页面了——当然,这是因为ADF在后台为我们做了很多事情。后面我们希望从Map组件入手,看看在一个会话中,ADF是怎么把后台的ArcGIS服务呈现到用户的浏览器,并使之能响应用户操作的。
当一个用户会话开始时,让我们首先看一下Map组件在服务器端绑定的WebMap对象,WebMap对象的定义我们可以在“/WebContent/WEB-INF/context-attributes.xml”中找到。在前面我们已经知道,一个JSF应用在启动的时候会读取“faces-config.xml”文件来配置应用;这里的“context-attributes.xml”虽然不是默认配置文件,但是在这里它也会被读取,因为在“/WebContent/WEB-INF/web.xml”中有如下的配置:

前面我们已经提过,“web.xml”是Java Web工程的部署描述文件,上述这段“web.xml”中的配置会指定JSF应用启动时需要读取的若干配置文件路径,“context-attributes.xml”赫然名列其中。对了,你也可以自己新建一个配置文件,然后在这里进行一下配置,比如我习惯新建一个“context-resources.xml”文件来定义WebContext需要的所有服务资源,然后再在“web.xml”中添加如上虚线框中的内容。
在“context-attributes.xml”中首先就是对WebMap的定义:

这段配置文件的含义就是:在应用中,在有需要的时候会实例化一个名为map的WebMap对象供使用,并且其地图输出格式为24位的PNG图片。
从图 3的结构我们可以看到,这个WebMap对象将会被放置到WebContext的attributes中进行使用和管理。比如按照下面这段“faces-config.xml”中的定义,当得到一个WebContext对象mapContext以后,我们就可以通过mapContext.attributes["map"]就可以得到WebMap对象了:

好,目前为止我们已经知道了在一个会话中,Map组件在服务器上绑定的WebMap对象如何生成,并且如何引用了。下面让我们看看Map组件和WebMap怎么配合渲染成浏览器中的一个地图的。
前面在JSP页面中我们是通过“a:map”标签来定义一个Map组件的,实际在ADF中,其对应的JSF组件为MapControl 。MapControl中包含WebMap、MapRenderer 、XslUrl等属性,WebMap负责维护地图的功能,MapRenderer负责将其输出成XML,最后再通过XslUrl指定的XSL文件将XML解释为DHTML输出。
打开“/src/xsl”目录,我们会发现下面有很多“*.xsl”文件,其中的“map.xsl”就是我们的MapControl渲染时候默认使用的XSL文件。当有用户向服务器发送请求的时候,JSF会首先创建或者还原MapControl这个视图对象,然后再使用其MapRenderer按照XSL文件渲染成DHTML响应输出给浏览器。
你可以运行下你的应用,然后查看地图页面的源代码,从中会发现类似以下的代码,这些就是Map组件的MapRenderer渲染的。

在这里出现了EsriMap这个JavaScript类, 它将会实例化浏览器端的地图对象。在下面这一节里,我们将会对EsriMap做详细解释。这里先稍微做一下总结,如图 8,借用帮助里的一张图片,应该可以帮助你了解ADF组件的基本原理。
以上几个组件的使用非常简单,不需要很多设置就可以搭起一个有模有样的WebGIS页面了——当然,这是因为ADF在后台为我们做了很多事情。后面我们希望从Map组件入手,看看在一个会话中,ADF是怎么把后台的ArcGIS服务呈现到用户的浏览器,并使之能响应用户操作的。
当一个用户会话开始时,让我们首先看一下Map组件在服务器端绑定的WebMap对象,WebMap对象的定义我们可以在“/WebContent/WEB-INF/context-attributes.xml”中找到。在前面我们已经知道,一个JSF应用在启动的时候会读取“faces-config.xml”文件来配置应用;这里的“context-attributes.xml”虽然不是默认配置文件,但是在这里它也会被读取,因为在“/WebContent/WEB-INF/web.xml”中有如下的配置:

前面我们已经提过,“web.xml”是Java Web工程的部署描述文件,上述这段“web.xml”中的配置会指定JSF应用启动时需要读取的若干配置文件路径,“context-attributes.xml”赫然名列其中。对了,你也可以自己新建一个配置文件,然后在这里进行一下配置,比如我习惯新建一个“context-resources.xml”文件来定义WebContext需要的所有服务资源,然后再在“web.xml”中添加如上虚线框中的内容。
在“context-attributes.xml”中首先就是对WebMap的定义:

这段配置文件的含义就是:在应用中,在有需要的时候会实例化一个名为map的WebMap对象供使用,并且其地图输出格式为24位的PNG图片。
从图 3的结构我们可以看到,这个WebMap对象将会被放置到WebContext的attributes中进行使用和管理。比如按照下面这段“faces-config.xml”中的定义,当得到一个WebContext对象mapContext以后,我们就可以通过mapContext.attributes["map"]就可以得到WebMap对象了:

好,目前为止我们已经知道了在一个会话中,Map组件在服务器上绑定的WebMap对象如何生成,并且如何引用了。下面让我们看看Map组件和WebMap怎么配合渲染成浏览器中的一个地图的。
前面在JSP页面中我们是通过“a:map”标签来定义一个Map组件的,实际在ADF中,其对应的JSF组件为MapControl 。MapControl中包含WebMap、MapRenderer 、XslUrl等属性,WebMap负责维护地图的功能,MapRenderer负责将其输出成XML,最后再通过XslUrl指定的XSL文件将XML解释为DHTML输出。
打开“/src/xsl”目录,我们会发现下面有很多“*.xsl”文件,其中的“map.xsl”就是我们的MapControl渲染时候默认使用的XSL文件。当有用户向服务器发送请求的时候,JSF会首先创建或者还原MapControl这个视图对象,然后再使用其MapRenderer按照XSL文件渲染成DHTML响应输出给浏览器。
你可以运行下你的应用,然后查看地图页面的源代码,从中会发现类似以下的代码,这些就是Map组件的MapRenderer渲染的。

在这里出现了EsriMap这个JavaScript类, 它将会实例化浏览器端的地图对象。在下面这一节里,我们将会对EsriMap做详细解释。这里先稍微做一下总结,如图 8,借用帮助里的一张图片,应该可以帮助你了解ADF组件的基本原理。
2009年4月3日星期五
ArcGIS Server Java ADF 案例教程 10
几个ADF常用组件
如图 6所示,ADF中几个与地图相关的组件的命名和功能都和ArcEngine很类似,主要包括Map、Toolbar、TOC、Overview等。我们先大致看一下这几个组件的用法和大概功能。
Map应该是所有ADF应用中都要使用的组件,它是一个地图的容器,内置了对各种地图服务的使用和操作。它在服务器端绑定的是一个WebMap 对象。以下标签在页面中添加了一个Map组件:

Toolbar是绑定到Map的工具条,在Toolbar中可以添加Tool和Command以实现相应的地图操作,而它本身仅仅是一个容器。以下标签在页面中添加了一个Toolbar组件,同时绑定到id为“map”的Map组件,并在其中放置了一个可以设置当前地图操作为“Pan”的Tool:

TOC是Table Of Contents的缩写,它提供一个面板来显示当前地图的图层信息,并可以进行隐藏/显示图层、缩放到图层等操作。它在服务器端绑定的是一个WebToc 对象。以下标签在页面中添加了一个TOC组件,同时绑定到id为“map”的Map组件:

Overview提供鹰眼图功能,它在一个全图的地图上用一个矩形框来表示当前地图的视图范围。它在服务器端绑定的是一个WebOverview 对象。以下标签在页面中添加了一个Overview组件,同时绑定到id为“map”的Map组件:
如图 6所示,ADF中几个与地图相关的组件的命名和功能都和ArcEngine很类似,主要包括Map、Toolbar、TOC、Overview等。我们先大致看一下这几个组件的用法和大概功能。
Map应该是所有ADF应用中都要使用的组件,它是一个地图的容器,内置了对各种地图服务的使用和操作。它在服务器端绑定的是一个WebMap 对象。以下标签在页面中添加了一个Map组件:

Toolbar是绑定到Map的工具条,在Toolbar中可以添加Tool和Command以实现相应的地图操作,而它本身仅仅是一个容器。以下标签在页面中添加了一个Toolbar组件,同时绑定到id为“map”的Map组件,并在其中放置了一个可以设置当前地图操作为“Pan”的Tool:

TOC是Table Of Contents的缩写,它提供一个面板来显示当前地图的图层信息,并可以进行隐藏/显示图层、缩放到图层等操作。它在服务器端绑定的是一个WebToc 对象。以下标签在页面中添加了一个TOC组件,同时绑定到id为“map”的Map组件:

Overview提供鹰眼图功能,它在一个全图的地图上用一个矩形框来表示当前地图的视图范围。它在服务器端绑定的是一个WebOverview 对象。以下标签在页面中添加了一个Overview组件,同时绑定到id为“map”的Map组件:
ArcGIS Server Java ADF 案例教程 9
ADF中的服务
ADF的后台都是各种不同的ArcGIS服务,一个ADF应用所依赖的就是这些服务提供的功能。这个小节让我们看一下服务是怎么配置到ADF应用,并和ADF对象关联起来的。
比如我现在发布了一个名为“World”的地图服务(Map Service),在ADF应用中准备通过Internet的方式进行连接。在当前的JSP文件上右键菜单,选择【ArcGIS】-【Configure Map Service…】后,如图 6所示添加地图服务。
在添加服务以后,让我们打开“faces-config.xml”来看一下ArcGIS Eclipse Plugin帮我们生成的配置(在此基础上我对一些名称作了修改):


在ADF中,服务是作为资源(Resource)登上舞台的。上面这段配置定义了一个名为agswsWorld的AGSMapResource对象,事实上,这个Resource对象关联的就是我们前面发布的“World”这个地图服务。在这里需要注意的是“functionalities”标签,“functionalities”中定义了当前这个资源可以使用的功能,比如一个地图服务显然可以进行查询,因此在我们的“functionalities”中定义了一个属性“query”,它指向一个名为“agsQuery”的AGSQueryFunctionality 对象(打开“/WebContext/WEB-INF/ functionalities/ags-functionalities.xml”可以发现它的踪迹),它提供了地图查询功能。
这个agswsWorld最后会被添加到WebContext的resources属性中去。通常在“faces-config.xml”中会以如下的方式定义:

稍微总结一下,ADF会根据不同的ArcGIS服务类型生成对应的AGSxxxResource对象进行关联,服务包含各种不同的功能可供使用。这些Resource对象根据需要会被添加到WebContext的“resources”属性中去进行管理和使用。以上这些关于Resource的内容默认都集中在“/WebContext/WEB-INF/“目录下的 “faces-config.xml”和“functionalities/*-functionalities.xml”中进行定义的。
ADF的后台都是各种不同的ArcGIS服务,一个ADF应用所依赖的就是这些服务提供的功能。这个小节让我们看一下服务是怎么配置到ADF应用,并和ADF对象关联起来的。
比如我现在发布了一个名为“World”的地图服务(Map Service),在ADF应用中准备通过Internet的方式进行连接。在当前的JSP文件上右键菜单,选择【ArcGIS】-【Configure Map Service…】后,如图 6所示添加地图服务。
在添加服务以后,让我们打开“faces-config.xml”来看一下ArcGIS Eclipse Plugin帮我们生成的配置(在此基础上我对一些名称作了修改):


在ADF中,服务是作为资源(Resource)登上舞台的。上面这段配置定义了一个名为agswsWorld的AGSMapResource对象,事实上,这个Resource对象关联的就是我们前面发布的“World”这个地图服务。在这里需要注意的是“functionalities”标签,“functionalities”中定义了当前这个资源可以使用的功能,比如一个地图服务显然可以进行查询,因此在我们的“functionalities”中定义了一个属性“query”,它指向一个名为“agsQuery”的AGSQueryFunctionality 对象(打开“/WebContext/WEB-INF/ functionalities/ags-functionalities.xml”可以发现它的踪迹),它提供了地图查询功能。
这个agswsWorld最后会被添加到WebContext的resources属性中去。通常在“faces-config.xml”中会以如下的方式定义:

稍微总结一下,ADF会根据不同的ArcGIS服务类型生成对应的AGSxxxResource对象进行关联,服务包含各种不同的功能可供使用。这些Resource对象根据需要会被添加到WebContext的“resources”属性中去进行管理和使用。以上这些关于Resource的内容默认都集中在“/WebContext/WEB-INF/“目录下的 “faces-config.xml”和“functionalities/*-functionalities.xml”中进行定义的。
订阅:
博文 (Atom)


















