[76] | 1 | /* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
---|
| 2 | * full list of contributors). Published under the Clear BSD license. |
---|
| 3 | * See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
---|
| 4 | * full text of the license. */ |
---|
| 5 | |
---|
| 6 | /** |
---|
| 7 | * @requires OpenLayers/Format/XML.js |
---|
| 8 | */ |
---|
| 9 | |
---|
| 10 | /** |
---|
| 11 | * Class: OpenLayers.Format.WMSGetFeatureInfo |
---|
| 12 | * Class to read GetFeatureInfo responses from Web Mapping Services |
---|
| 13 | * |
---|
| 14 | * Inherits from: |
---|
| 15 | * - <OpenLayers.Format.XML> |
---|
| 16 | */ |
---|
| 17 | OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { |
---|
| 18 | |
---|
| 19 | /** |
---|
| 20 | * APIProperty: layerIdentifier |
---|
| 21 | * {String} All xml nodes containing this search criteria will populate an |
---|
| 22 | * internal array of layer nodes. |
---|
| 23 | */ |
---|
| 24 | layerIdentifier: '_layer', |
---|
| 25 | |
---|
| 26 | /** |
---|
| 27 | * APIProperty: featureIdentifier |
---|
| 28 | * {String} All xml nodes containing this search criteria will populate an |
---|
| 29 | * internal array of feature nodes for each layer node found. |
---|
| 30 | */ |
---|
| 31 | featureIdentifier: '_feature', |
---|
| 32 | |
---|
| 33 | /** |
---|
| 34 | * Property: regExes |
---|
| 35 | * Compiled regular expressions for manipulating strings. |
---|
| 36 | */ |
---|
| 37 | regExes: { |
---|
| 38 | trimSpace: (/^\s*|\s*$/g), |
---|
| 39 | removeSpace: (/\s*/g), |
---|
| 40 | splitSpace: (/\s+/), |
---|
| 41 | trimComma: (/\s*,\s*/g) |
---|
| 42 | }, |
---|
| 43 | |
---|
| 44 | /** |
---|
| 45 | * Property: gmlFormat |
---|
| 46 | * {<OpenLayers.Format.GML>} internal GML format for parsing geometries |
---|
| 47 | * in msGMLOutput |
---|
| 48 | */ |
---|
| 49 | gmlFormat: null, |
---|
| 50 | |
---|
| 51 | /** |
---|
| 52 | * Constructor: OpenLayers.Format.WMSGetFeatureInfo |
---|
| 53 | * Create a new parser for WMS GetFeatureInfo responses |
---|
| 54 | * |
---|
| 55 | * Parameters: |
---|
| 56 | * options - {Object} An optional object whose properties will be set on |
---|
| 57 | * this instance. |
---|
| 58 | */ |
---|
| 59 | initialize: function(options) { |
---|
| 60 | OpenLayers.Format.XML.prototype.initialize.apply(this, arguments); |
---|
| 61 | OpenLayers.Util.extend(this, options); |
---|
| 62 | this.options = options; |
---|
| 63 | }, |
---|
| 64 | |
---|
| 65 | /** |
---|
| 66 | * APIMethod: read |
---|
| 67 | * Read WMS GetFeatureInfo data from a string, and return an array of features |
---|
| 68 | * |
---|
| 69 | * Parameters: |
---|
| 70 | * data - {String} or {DOMElement} data to read/parse. |
---|
| 71 | * |
---|
| 72 | * Returns: |
---|
| 73 | * {Array(<OpenLayers.Feature.Vector>)} An array of features. |
---|
| 74 | */ |
---|
| 75 | read: function(data) { |
---|
| 76 | var result; |
---|
| 77 | if(typeof data == "string") { |
---|
| 78 | data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); |
---|
| 79 | } |
---|
| 80 | var root = data.documentElement; |
---|
| 81 | if(root) { |
---|
| 82 | var scope = this; |
---|
| 83 | var read = this["read_" + root.nodeName]; |
---|
| 84 | if(read) { |
---|
| 85 | result = read.call(this, root); |
---|
| 86 | } else { |
---|
| 87 | // fall-back to GML since this is a common output format for WMS |
---|
| 88 | // GetFeatureInfo responses |
---|
| 89 | result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data); |
---|
| 90 | } |
---|
| 91 | } else { |
---|
| 92 | result = data; |
---|
| 93 | } |
---|
| 94 | return result; |
---|
| 95 | }, |
---|
| 96 | |
---|
| 97 | |
---|
| 98 | /** |
---|
| 99 | * Method: read_msGMLOutput |
---|
| 100 | * Parse msGMLOutput nodes. |
---|
| 101 | * |
---|
| 102 | * Parameters: |
---|
| 103 | * data - {DOMElement} |
---|
| 104 | * |
---|
| 105 | * Returns: |
---|
| 106 | * {Array} |
---|
| 107 | */ |
---|
| 108 | read_msGMLOutput: function(data) { |
---|
| 109 | var response = []; |
---|
| 110 | var layerNodes = this.getSiblingNodesByTagCriteria(data, |
---|
| 111 | this.layerIdentifier); |
---|
| 112 | if (layerNodes) { |
---|
| 113 | for (var i=0, len=layerNodes.length; i<len; ++i) { |
---|
| 114 | var node = layerNodes[i]; |
---|
| 115 | var layerName = node.nodeName; |
---|
| 116 | if (node.prefix) { |
---|
| 117 | layerName = layerName.split(':')[1]; |
---|
| 118 | } |
---|
| 119 | var layerName = layerName.replace(this.layerIdentifier, ''); |
---|
| 120 | var featureNodes = this.getSiblingNodesByTagCriteria(node, |
---|
| 121 | this.featureIdentifier); |
---|
| 122 | if (featureNodes) { |
---|
| 123 | for (var j = 0; j < featureNodes.length; j++) { |
---|
| 124 | var featureNode = featureNodes[j]; |
---|
| 125 | var geomInfo = this.parseGeometry(featureNode); |
---|
| 126 | var attributes = this.parseAttributes(featureNode); |
---|
| 127 | var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, |
---|
| 128 | attributes, null); |
---|
| 129 | feature.bounds = geomInfo.bounds; |
---|
| 130 | feature.type = layerName; |
---|
| 131 | response.push(feature); |
---|
| 132 | } |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | } |
---|
| 136 | return response; |
---|
| 137 | }, |
---|
| 138 | |
---|
| 139 | /** |
---|
| 140 | * Method: read_FeatureInfoResponse |
---|
| 141 | * Parse FeatureInfoResponse nodes. |
---|
| 142 | * |
---|
| 143 | * Parameters: |
---|
| 144 | * data - {DOMElement} |
---|
| 145 | * |
---|
| 146 | * Returns: |
---|
| 147 | * {Array} |
---|
| 148 | */ |
---|
| 149 | read_FeatureInfoResponse: function(data) { |
---|
| 150 | var response = []; |
---|
| 151 | var featureNodes = this.getElementsByTagNameNS(data, '*', |
---|
| 152 | 'FIELDS'); |
---|
| 153 | |
---|
| 154 | for(var i=0, len=featureNodes.length;i<len;i++) { |
---|
| 155 | var featureNode = featureNodes[i]; |
---|
| 156 | var geom = null; |
---|
| 157 | |
---|
| 158 | var attributes = {}; |
---|
| 159 | for(var j=0, jlen=featureNode.attributes.length; j<jlen; j++) { |
---|
| 160 | var attribute = featureNode.attributes[j]; |
---|
| 161 | attributes[attribute.nodeName] = attribute.nodeValue; |
---|
| 162 | } |
---|
| 163 | |
---|
| 164 | response.push( |
---|
| 165 | new OpenLayers.Feature.Vector(geom, attributes, null) |
---|
| 166 | ); |
---|
| 167 | } |
---|
| 168 | return response; |
---|
| 169 | }, |
---|
| 170 | |
---|
| 171 | /** |
---|
| 172 | * Method: getSiblingNodesByTagCriteria |
---|
| 173 | * Recursively searches passed xml node and all it's descendant levels for |
---|
| 174 | * nodes whose tagName contains the passed search string. This returns an |
---|
| 175 | * array of all sibling nodes which match the criteria from the highest |
---|
| 176 | * hierarchial level from which a match is found. |
---|
| 177 | * |
---|
| 178 | * Parameters: |
---|
| 179 | * node - {DOMElement} An xml node |
---|
| 180 | * criteria - {String} Search string which will match some part of a tagName |
---|
| 181 | * |
---|
| 182 | * Returns: |
---|
| 183 | * Array({DOMElement)) An array of sibling xml nodes |
---|
| 184 | */ |
---|
| 185 | getSiblingNodesByTagCriteria: function(node, criteria){ |
---|
| 186 | var nodes = []; |
---|
| 187 | var children, tagName, n, matchNodes, child; |
---|
| 188 | if (node && node.hasChildNodes()) { |
---|
| 189 | children = node.childNodes; |
---|
| 190 | n = children.length; |
---|
| 191 | |
---|
| 192 | for(var k=0; k<n; k++){ |
---|
| 193 | child = children[k]; |
---|
| 194 | while (child && child.nodeType != 1) { |
---|
| 195 | child = child.nextSibling; |
---|
| 196 | k++; |
---|
| 197 | } |
---|
| 198 | tagName = (child ? child.nodeName : ''); |
---|
| 199 | if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { |
---|
| 200 | nodes.push(child); |
---|
| 201 | } else { |
---|
| 202 | matchNodes = this.getSiblingNodesByTagCriteria( |
---|
| 203 | child, criteria); |
---|
| 204 | |
---|
| 205 | if(matchNodes.length > 0){ |
---|
| 206 | (nodes.length == 0) ? |
---|
| 207 | nodes = matchNodes : nodes.push(matchNodes); |
---|
| 208 | } |
---|
| 209 | } |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | } |
---|
| 213 | return nodes; |
---|
| 214 | }, |
---|
| 215 | |
---|
| 216 | /** |
---|
| 217 | * Method: parseAttributes |
---|
| 218 | * |
---|
| 219 | * Parameters: |
---|
| 220 | * node - {<DOMElement>} |
---|
| 221 | * |
---|
| 222 | * Returns: |
---|
| 223 | * {Object} An attributes object. |
---|
| 224 | * |
---|
| 225 | * Notes: |
---|
| 226 | * Assumes that attributes are direct child xml nodes of the passed node |
---|
| 227 | * and contain only a single text node. |
---|
| 228 | */ |
---|
| 229 | parseAttributes: function(node){ |
---|
| 230 | var attributes = {}; |
---|
| 231 | if (node.nodeType == 1) { |
---|
| 232 | var children = node.childNodes; |
---|
| 233 | var n = children.length; |
---|
| 234 | for (var i = 0; i < n; ++i) { |
---|
| 235 | var child = children[i]; |
---|
| 236 | if (child.nodeType == 1) { |
---|
| 237 | var grandchildren = child.childNodes; |
---|
| 238 | if (grandchildren.length == 1) { |
---|
| 239 | var grandchild = grandchildren[0]; |
---|
| 240 | if (grandchild.nodeType == 3 || |
---|
| 241 | grandchild.nodeType == 4) { |
---|
| 242 | var name = (child.prefix) ? |
---|
| 243 | child.nodeName.split(":")[1] : child.nodeName; |
---|
| 244 | var value = grandchild.nodeValue.replace( |
---|
| 245 | this.regExes.trimSpace, ""); |
---|
| 246 | attributes[name] = value; |
---|
| 247 | } |
---|
| 248 | } |
---|
| 249 | } |
---|
| 250 | } |
---|
| 251 | } |
---|
| 252 | return attributes; |
---|
| 253 | }, |
---|
| 254 | |
---|
| 255 | /** |
---|
| 256 | * Method: parseGeometry |
---|
| 257 | * Parse the geometry and the feature bounds out of the node using |
---|
| 258 | * Format.GML |
---|
| 259 | * |
---|
| 260 | * Parameters: |
---|
| 261 | * node - {<DOMElement>} |
---|
| 262 | * |
---|
| 263 | * Returns: |
---|
| 264 | * {Object} An object containing the geometry and the feature bounds |
---|
| 265 | */ |
---|
| 266 | parseGeometry: function(node) { |
---|
| 267 | // we need to use the old Format.GML parser since we do not know the |
---|
| 268 | // geometry name |
---|
| 269 | if (!this.gmlFormat) { |
---|
| 270 | this.gmlFormat = new OpenLayers.Format.GML(); |
---|
| 271 | } |
---|
| 272 | var feature = this.gmlFormat.parseFeature(node); |
---|
| 273 | var geometry, bounds = null; |
---|
| 274 | if (feature) { |
---|
| 275 | geometry = feature.geometry && feature.geometry.clone(); |
---|
| 276 | bounds = feature.bounds && feature.bounds.clone(); |
---|
| 277 | feature.destroy(); |
---|
| 278 | } |
---|
| 279 | return {geometry: geometry, bounds: bounds}; |
---|
| 280 | }, |
---|
| 281 | |
---|
| 282 | CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" |
---|
| 283 | |
---|
| 284 | }); |
---|