自定义 WMS GetFeatureInfo 响应

WMS 服务支持一系列操作,如 GetCapabilities、GetMap、GetStyles 等,允许客户端应用程序通过向服务的 URL 附加各种参数来使用此服务。GetFeatureInfo 操作以同样的方式工作,它用于以多种格式(如 HTML、XML 和纯文本)返回地图中所查询要素的属性。

以下是 GetFeatureInfo 请求及其默认的 HTML 格式响应的示例:

请求

http://gisserver.domain.com/arcgis/services/ihs_petroleum/MapServer/WMSServer?&service=WMS&version=1.1.0&request=GetFeatureInfo&layers=fields&query_layers=fields&styles=&bbox=47.130647,8.931116,48.604188,29.54223&srs=EPSG:4326&feature_count=10&x=562&y=193&height=445&width=1073&info_format=text/html&

响应

默认的 HTML GetFeatureInfo 响应

在很多情况下,默认的 HTML、XML 或纯文本响应即满足需求,但也可能会存在想要自定义响应格式或方案以执行特定业务逻辑的情况。例如,由于互操作的原因,您可能希望以标准模式(如 GML 或 GeoJSON)返回要素信息。

XSLT 模板

可扩展样式表语言转换 (XSLT) 模板可以基于 WMS GetFeatureInfo 响应生成便于阅读的输出结果。例如,向服务器发送 WMS GetFeatureInfo 请求时,服务器以 XML 格式返回所请求元素的响应。然后,XSLT 模板将 XML“转换”为指定的格式(如 HTML 或纯文本),从而实现了最后响应的可读性。

查看 WMS GetFeatureInfo XML 响应以及 ArcGIS Server 安装时生成的默认 XSLT 模板,将有助于更好地理解如何自定义 GetFeatureInfo 响应。以下部分将做详细介绍。

GetFeatureInfo XML 响应

以下示例是 XML 格式的 WMS 服务中的 GetFeatureInfo 响应:

<?xml version="1.0" encoding="UTF-8"?>
<esri_wms:FeatureInfoResponse version="1.3.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:esri_wms="http://www.esri.com/wms" xmlns="http://www.esri.com/wms">
  <esri_wms:FeatureInfoCollection layername="fields">
    <esri_wms:FeatureInfo>
      <esri_wms:Field>
        <esri_wms:FieldName><![CDATA[OBJECTID]]></esri_wms:FieldName>
        <esri_wms:FieldValue><![CDATA[1]]></esri_wms:FieldValue>
      </esri_wms:Field>
      <esri_wms:Field>
        <esri_wms:FieldName><![CDATA[Shape]]></esri_wms:FieldName>
        <esri_wms:FieldValue><![CDATA[Polygon]]></esri_wms:FieldValue>
      </esri_wms:Field>
      <esri_wms:Field>
        <esri_wms:FieldName><![CDATA[Shape_Area]]></esri_wms:FieldName>
        <esri_wms:FieldValue><![CDATA[0.009079]]></esri_wms:FieldValue>
      </esri_wms:Field>
        ...
        <!-- there could be more <esri_wms:Field> -->
        ...
    </esri_wms:FeatureInfo>
    ...
    <!-- there could be more <esri_wms:FeatureInfo> -->
    ...
  </esri_wms:FeatureInfoCollection>
  ...
  <!-- there could be more <esri_wms:FeatureInfoCollection> -->
  ...
</esri_wms:FeatureInfoResponse>

请注意以下细节:

  • 根标签 <FeatureInfoResponse> 可包含多个 <FeatureInfoCollection> 元素。
  • 每个 <FeatureInfoCollection> 元素包含从单个 WMS 图层中识别出的所有要素的属性字段和值。
  • 每个识别出的要素的信息包含在<FeatureInfo> 标签中。请注意,每个字段的“名称-值”均成对出现。

默认的 XSLT 模板

ArcGIS Server 附带的 XSLT 模板用于支持 WMS 的功能文件中列出的格式。例如,如果打开这些模板的目录(位于 <ArcGIS Server 安装位置>/Styles/WMS),将会看到以下内容:

  • featureinfo_application_geojson.xsl
  • featureinfo_application_vnd.esri.wms_featureinfo_xml.xsl
  • featureinfo_application_vnd.ogc.wms_xml.xsl
  • featureinfo_text_html.xsl
  • featureinfo_text_plain.xsl
  • featureinfo_text_xml.xsl

根据它们的文件名可以看出,每个模板用于以便于阅读的格式(如 GeoJSON、纯文本和 XML)生成一个默认的 GetFeatureInfo 响应。

本主题起始位置显示的带蓝色标题的示例 HTML 表就是使用默认 XSLT HTML 模板生成的。另外,如果要将原始 XML 作为响应,可以将 GetFeatureInfo 请求参数 INFO_FORMAT 设置为 application/vnd.esri.wms_raw_xml。可以使用此方法来创建自定义 XSLT 模板。

自定义 GetFeatureInfo 响应

现在,我们已对 GetFeatureInfo 响应 XML 和 XSLT 模板有了初步的了解,接下来就可以深入研究两种自定义 WMS GetFeatureInfo 响应的方法。

修改默认的 XSLT 模板

第一种方法是直接修改 XSLT 模板。例如,如果在文本编辑器中打开 HTML 模板 featureinfo_text_html.xsl,然后将 <Style> 标签替换为以下 XML 代码,则表格标题将显示为红色:

示例 XML

<style type="text/css">
          table, th, td {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-size: 80%;            
            color: #333333
          }             
          th, td {
            valign: top;
            text-align: center;
          }          
          th {
            background-color: #ffb7b7
          }
          caption {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-weight: bold;
            font-size: 80%;      
            text-align: left;      
            color: #333333;    
          }
        </style>

示例响应

表格标题为红色的 HTML GetFeatureInfo 响应

但是,如果修改这些模板,将对通过 ArcGIS Server 发布的所有 WMS 造成直接影响。因此,请避免将任何特定于地图服务的逻辑放到这些模板中。

使用 xslt_template 参数

自定义 GetFeatureInfo 响应的另一种方法是,使用 xslt_template 参数来覆盖默认 XSLT 模板的行为。xslt_template 是一个特定于 Esri 的参数,可在 XSLT 模板文件的 URL 中设置。在 URL 字符串中指定该模板时,WMS 将覆盖默认模板并使用指定的模板。如果已建立了一个自定义模板,这将是在 GetFeatureInfo 响应中利用模板的最佳方法。

注注:

使用 xslt_template 参数时,XSLT 模板不必遵循与默认模板相同的命名约定,但它需要经由 URL 提供。指定本地路径或网络共享会导致请求失败。

以下是含有 xslt_template 参数的 GetFeatureInfo 请求的示例:

http://gisserver.domain.com/arcgis/services/ihs_petroleum/MapServer/WMSServer?&service=WMS&version=1.1.1&request=GetFeatureInfo&layers=pipelines&query_layers=pipelines&styles=&bbox=47.119661,28.931116,48.593202,29.54223&srs=EPSG:4326&feature_count=10&x=389&y=120&height=445&width=1073&info_format=text/plain&xsl_template=http://server/resources/xsl/featureinfo_application_geojson.xsl

以上 URL 引用了一个将覆盖默认模板的外部模板。自定义模板 XML 如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:esri_wms="http://www.esri.com/wms" xmlns="http://www.esri.com/wms">  
  <xsl:output 
    method="text" 
    indent="yes" 
    encoding="ISO-8859-1"/>     
  
  <xsl:template match="/">{  
  "type": "FeatureCollection",
  "features": [<xsl:for-each select="esri_wms:FeatureInfoResponse/esri_wms:FeatureInfoCollection/esri_wms:FeatureInfo">
    {
      "type": "Feature",
      "properties": 
        {<xsl:for-each select="esri_wms:Field">                  
          "<xsl:value-of select="esri_wms:FieldName"/>":"<xsl:value-of select="esri_wms:FieldValue"/>",</xsl:for-each>
        },
      "layerName":"<xsl:value-of select="../@layername"/>"  
    },</xsl:for-each>
  ]
}
  </xsl:template>
</xsl:stylesheet>

上述模板可以使 WMS 以纯文本 GeoJSON 格式(而不使用默认 HTML 格式)返回其 GetFeatureInfo 响应。GeoJSON 可被多种 JavaScript 库解析,从而以便于阅读的格式将响应集成到网页中。

以下是一个 GetFeatureInfo GeoJSON 响应示例,作为 OpenLayers Web 地图应用程序中 Ext.Grid 的数据源:

作为 OpenLayers Web 地图应用程序中 Ext.Grid 数据源的 GetFeatureInfo GeoJSON 响应

基于之前的示例,以下示例模板可用于在响应中嵌入视频对象:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:esri_wms="http://www.esri.com/wms" xmlns="http://www.esri.com/wms">
  <!--
    <%@page contentType="text/html" pageEncoding="UTF-8"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  -->    
  <xsl:output 
    method="html" 
    indent="yes" 
    encoding="UTF-8" 
    omit-xml-declaration="yes"/> 
  <xsl:template match="/">    
  <!--<html>
      <head>-->
        <style type="text/css">
          table, th, td {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-size: 80%;            
            color: #333333
          }             
          th, td {
            valign: top;
            text-align: center;
          }          
          th {
            background-color: #aed7ff;
          }
          caption {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-weight: bold;
            font-size: 80%;      
            text-align: left;      
            color: #333333;
            background-color: #aed7ff;            
          }
        </style>
      
    <!--</head>
      <body>-->  
      <div>        
        <xsl:for-each select="esri_wms:FeatureInfoResponse/esri_wms:FeatureInfoCollection">                              
          <table width="100%" cellpadding="0" cellspacing="0" border="1">
            <tbody>                          
              <caption>layer names: '<xsl:value-of select="@layername"/>'</caption>
              <xsl:for-each select="esri_wms:FeatureInfo[1]/esri_wms:Field">
                <xsl:variable name="fieldName" select="esri_wms:FieldName"/>
                <xsl:variable name="fieldValue" select="esri_wms:FieldValue"/>                              
                <xsl:if test="$fieldName = 'PLOT_SYMBOL_GROUP'">                  
                  <xsl:choose>
                    <xsl:when test="$fieldValue = 2">                      
                      <tr>
                        <td>
                          wiki link
                        </td>
                        <td>
                          <a target="_blank">
                            <xsl:attribute name="href">
                              http://en.wikipedia.org/wiki/Oil_well
                            </xsl:attribute>
                            Oil Well
                          </a>  
                        </td>
                      </tr>                      
                      <tr>
                        <td>
                          video
                        </td>
                        <td>
                          <div>
                          <object width="425" height="344">
                            <param name="movie" value="http://www.youtube.com/v/HVxsbb1lDsQ"></param>
                            <param name="allowFullScreen" value="true"></param>
                            <param name="allowscriptaccess" value="always"></param>
                            <embed src="http://www.youtube.com/v/HVxsbb1lDsQ" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed>
                          </object>
                          </div>
                        </td>
                      </tr>
                    </xsl:when>                        
                  </xsl:choose>                    
                </xsl:if>                                    
              </xsl:for-each>                              
            </tbody>
          </table>          
        </xsl:for-each>
      </div>
    <!--</body>
    </html>-->
  </xsl:template>

以下是一个 GetFeatureInfo 响应示例,其中 OpenLayers Web 地图应用程序中嵌入了视频:

OpenLayers Web 地图应用程序中嵌入了视频的 GetFeatureInfo 响应

以下是一个更全面的示例,通过嵌入 JavaScript 代码片段对 GetFeatureInfo 响应做较大范围的自定义。该代码会在 Google 地球插件 Web 应用程序中播放内容。请注意,此 XSLT 模板中存在针对特定 WMS 服务的逻辑。因此,应始终通过 xslt_template 参数引用此模板。不应将其设为默认模板。

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:esri_wms="http://www.esri.com/wms" xmlns="http://www.esri.com/wms">
  <!--
    <%@page contentType="text/html" pageEncoding="UTF-8"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  -->    
  <xsl:output 
    method="html" 
    indent="yes" 
    encoding="UTF-8" 
    omit-xml-declaration="yes"/> 
  <xsl:template match="/">    
  <!--<html>
      <head>-->
        <style type="text/css">
          table, th, td {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-size: 80%;            
            color: #333333
          }             
          th, td {
            valign: top;
            text-align: center;
          }          
          th {
            background-color: #aed7ff;
          }
          caption {
            border:1px solid #e5e5e5;
            border-collapse:collapse;
            font-family: arial;          
            font-weight: bold;
            font-size: 80%;      
            text-align: left;      
            color: #333333;
            background-color: #aed7ff;            
          }
        </style>
      
    <!--</head>
      <body>-->  
      <div>        
        <xsl:for-each select="esri_wms:FeatureInfoResponse/esri_wms:FeatureInfoCollection">                              
          <table width="100%" cellpadding="0" cellspacing="0" border="1">
            <tbody>                          
              <caption>layer names: '<xsl:value-of select="@layername"/>'</caption>
              <xsl:for-each select="esri_wms:FeatureInfo[1]/esri_wms:Field">
                <xsl:variable name="fieldName" select="esri_wms:FieldName"/>
                <xsl:variable name="fieldValue" select="esri_wms:FieldValue"/>                
                <xsl:if test="$fieldName = 'WGS84_LONGITUDE'">
                  <xsl:variable name="lon" select="esri_wms:FieldValue"/>
                  <tr>
                    <td>lon</td>
                    <td><xsl:value-of select="$lon"/></td>
                    <script type="text/javascript">
                      popup_lon = '<xsl:value-of select="$lon"/>';
                    </script>
                  </tr>
                </xsl:if>
                <xsl:if test="$fieldName = 'WGS84_LATITUDE'">
                  <xsl:variable name="lat" select="esri_wms:FieldValue"/>
                  <tr>
                    <td>lat</td>
                    <td><xsl:value-of select="$lat"/></td>
                    <script type="text/javascript">
                      popup_lat = '<xsl:value-of select="$lat"/>';
                    </script>
                  </tr>
                </xsl:if>                                                                              
                <xsl:if test="$fieldName = 'PLOT_SYMBOL_GROUP'">                  
                  <xsl:choose>                    
                    <xsl:when test="$fieldValue = 14">                                                                                      
                      <tr>
                        <td>
                          3d map
                        </td>
                        <td>
                          <div id="map3d" style="width:384px;height:256px;"></div>
                          <script type="text/javascript">
                            google.earth.createInstance(
                              'map3d', 
                              function(instance) {
                                ge = instance;
                                ge.getWindow().setVisibility(true);
                                
                                var kmlStr = ''                                        
                                  + '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">'
                                  + '<gx:Tour>'
                                  + '<gx:Playlist><gx:FlyTo>'
                                  + '<gx:duration>20.0</gx:duration>'
                                  + '<LookAt>'
                                  + '<longitude>' + popup_lon + '</longitude>'
                                  + '<latitude>' + popup_lat + '</latitude>'
                                  + '<altitude>0</altitude>'
                                  + '<heading>0</heading>'
                                  + '<tilt>0</tilt>'
                                  + '<range>500</range>'
                                  + '<altitudeMode>relativeToGround</altitudeMode>'
                                  + '</LookAt>'
                                  + '</gx:FlyTo></gx:Playlist>'
                                  + '</gx:Tour>'
                                  + '</kml>';
                                        
                                var kmlObj = ge.parseKml(kmlStr);
                                ge.getTourPlayer().setTour(kmlObj);
                                ge.getTourPlayer().play();                                
                              }, 
                              function() {
                                
                              }
                            );
                          </script>
                        </td>
                      </tr>
                    </xsl:when>  
                  </xsl:choose>                    
                </xsl:if>                                    
              </xsl:for-each>                              
            </tbody>
          </table>          
        </xsl:for-each>
      </div>
    <!--</body>
    </html>-->
  </xsl:template>

6/13/2014