WMSGetFeatureInfo.js 9.6 KB

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