123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- import Cartographic from '../Core/Cartographic.js';
- import defined from '../Core/defined.js';
- import DeveloperError from '../Core/DeveloperError.js';
- import RuntimeError from '../Core/RuntimeError.js';
- import ImageryLayerFeatureInfo from './ImageryLayerFeatureInfo.js';
- /**
- * Describes the format in which to request GetFeatureInfo from a Web Map Service (WMS) server.
- *
- * @alias GetFeatureInfoFormat
- * @constructor
- *
- * @param {String} type The type of response to expect from a GetFeatureInfo request. Valid
- * values are 'json', 'xml', 'html', or 'text'.
- * @param {String} [format] The info format to request from the WMS server. This is usually a
- * MIME type such as 'application/json' or text/xml'. If this parameter is not specified, the provider will request 'json'
- * using 'application/json', 'xml' using 'text/xml', 'html' using 'text/html', and 'text' using 'text/plain'.
- * @param {Function} [callback] A function to invoke with the GetFeatureInfo response from the WMS server
- * in order to produce an array of picked {@link ImageryLayerFeatureInfo} instances. If this parameter is not specified,
- * a default function for the type of response is used.
- */
- function GetFeatureInfoFormat(type, format, callback) {
- //>>includeStart('debug', pragmas.debug);
- if (!defined(type)) {
- throw new DeveloperError('type is required.');
- }
- //>>includeEnd('debug');
- this.type = type;
- if (!defined(format)) {
- if (type === 'json') {
- format = 'application/json';
- } else if (type === 'xml') {
- format = 'text/xml';
- } else if (type === 'html') {
- format = 'text/html';
- } else if (type === 'text') {
- format = 'text/plain';
- }
- //>>includeStart('debug', pragmas.debug);
- else {
- throw new DeveloperError('format is required when type is not "json", "xml", "html", or "text".');
- }
- //>>includeEnd('debug');
- }
- this.format = format;
- if (!defined(callback)) {
- if (type === 'json') {
- callback = geoJsonToFeatureInfo;
- } else if (type === 'xml') {
- callback = xmlToFeatureInfo;
- } else if (type === 'html') {
- callback = textToFeatureInfo;
- } else if (type === 'text') {
- callback = textToFeatureInfo;
- }
- //>>includeStart('debug', pragmas.debug);
- else {
- throw new DeveloperError('callback is required when type is not "json", "xml", "html", or "text".');
- }
- //>>includeEnd('debug');
- }
- this.callback = callback;
- }
- function geoJsonToFeatureInfo(json) {
- var result = [];
- var features = json.features;
- for (var i = 0; i < features.length; ++i) {
- var feature = features[i];
- var featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = feature;
- featureInfo.properties = feature.properties;
- featureInfo.configureNameFromProperties(feature.properties);
- featureInfo.configureDescriptionFromProperties(feature.properties);
- // If this is a point feature, use the coordinates of the point.
- if (defined(feature.geometry) && feature.geometry.type === 'Point') {
- var longitude = feature.geometry.coordinates[0];
- var latitude = feature.geometry.coordinates[1];
- featureInfo.position = Cartographic.fromDegrees(longitude, latitude);
- }
- result.push(featureInfo);
- }
- return result;
- }
- var mapInfoMxpNamespace = 'http://www.mapinfo.com/mxp';
- var esriWmsNamespace = 'http://www.esri.com/wms';
- var wfsNamespace = 'http://www.opengis.net/wfs';
- var gmlNamespace = 'http://www.opengis.net/gml';
- function xmlToFeatureInfo(xml) {
- var documentElement = xml.documentElement;
- if (documentElement.localName === 'MultiFeatureCollection' && documentElement.namespaceURI === mapInfoMxpNamespace) {
- // This looks like a MapInfo MXP response
- return mapInfoXmlToFeatureInfo(xml);
- } else if (documentElement.localName === 'FeatureInfoResponse' && documentElement.namespaceURI === esriWmsNamespace) {
- // This looks like an Esri WMS response
- return esriXmlToFeatureInfo(xml);
- } else if (documentElement.localName === 'FeatureCollection' && documentElement.namespaceURI === wfsNamespace) {
- // This looks like a WFS/GML response.
- return gmlToFeatureInfo(xml);
- } else if (documentElement.localName === 'ServiceExceptionReport') {
- // This looks like a WMS server error, so no features picked.
- throw new RuntimeError(new XMLSerializer().serializeToString(documentElement));
- } else if (documentElement.localName === 'msGMLOutput') {
- return msGmlToFeatureInfo(xml);
- } else {
- // Unknown response type, so just dump the XML itself into the description.
- return unknownXmlToFeatureInfo(xml);
- }
- }
- function mapInfoXmlToFeatureInfo(xml) {
- var result = [];
- var multiFeatureCollection = xml.documentElement;
- var features = multiFeatureCollection.getElementsByTagNameNS(mapInfoMxpNamespace, 'Feature');
- for (var featureIndex = 0; featureIndex < features.length; ++featureIndex) {
- var feature = features[featureIndex];
- var properties = {};
- var propertyElements = feature.getElementsByTagNameNS(mapInfoMxpNamespace, 'Val');
- for (var propertyIndex = 0; propertyIndex < propertyElements.length; ++propertyIndex) {
- var propertyElement = propertyElements[propertyIndex];
- if (propertyElement.hasAttribute('ref')) {
- var name = propertyElement.getAttribute('ref');
- var value = propertyElement.textContent.trim();
- properties[name] = value;
- }
- }
- var featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = feature;
- featureInfo.properties = properties;
- featureInfo.configureNameFromProperties(properties);
- featureInfo.configureDescriptionFromProperties(properties);
- result.push(featureInfo);
- }
- return result;
- }
- function esriXmlToFeatureInfo(xml) {
- var featureInfoResponse = xml.documentElement;
- var result = [];
- var properties;
- var features = featureInfoResponse.getElementsByTagNameNS('*', 'FIELDS');
- if (features.length > 0) {
- // Standard esri format
- for (var featureIndex = 0; featureIndex < features.length; ++featureIndex) {
- var feature = features[featureIndex];
- properties = {};
- var propertyAttributes = feature.attributes;
- for (var attributeIndex = 0; attributeIndex < propertyAttributes.length; ++attributeIndex) {
- var attribute = propertyAttributes[attributeIndex];
- properties[attribute.name] = attribute.value;
- }
- result.push(imageryLayerFeatureInfoFromDataAndProperties(feature, properties));
- }
- } else {
- // Thredds format -- looks like esri, but instead of containing FIELDS, contains FeatureInfo element
- var featureInfoElements = featureInfoResponse.getElementsByTagNameNS('*', 'FeatureInfo');
- for (var featureInfoElementIndex = 0; featureInfoElementIndex < featureInfoElements.length; ++featureInfoElementIndex) {
- var featureInfoElement = featureInfoElements[featureInfoElementIndex];
- properties = {};
- // node.children is not supported in IE9-11, so use childNodes and check that child.nodeType is an element
- var featureInfoChildren = featureInfoElement.childNodes;
- for (var childIndex = 0; childIndex < featureInfoChildren.length; ++childIndex) {
- var child = featureInfoChildren[childIndex];
- if (child.nodeType === Node.ELEMENT_NODE) {
- properties[child.localName] = child.textContent;
- }
- }
- result.push(imageryLayerFeatureInfoFromDataAndProperties(featureInfoElement, properties));
- }
- }
- return result;
- }
- function gmlToFeatureInfo(xml) {
- var result = [];
- var featureCollection = xml.documentElement;
- var featureMembers = featureCollection.getElementsByTagNameNS(gmlNamespace, 'featureMember');
- for (var featureIndex = 0; featureIndex < featureMembers.length; ++featureIndex) {
- var featureMember = featureMembers[featureIndex];
- var properties = {};
- getGmlPropertiesRecursively(featureMember, properties);
- result.push(imageryLayerFeatureInfoFromDataAndProperties(featureMember, properties));
- }
- return result;
- }
- // msGmlToFeatureInfo is similar to gmlToFeatureInfo, but assumes different XML structure
- // eg. <msGMLOutput> <ABC_layer> <ABC_feature> <foo>bar</foo> ... </ABC_feature> </ABC_layer> </msGMLOutput>
- function msGmlToFeatureInfo(xml) {
- var result = [];
- // Find the first child. Except for IE, this would work:
- // var layer = xml.documentElement.children[0];
- var layer;
- var children = xml.documentElement.childNodes;
- for (var i = 0; i < children.length; i++) {
- if (children[i].nodeType === Node.ELEMENT_NODE) {
- layer = children[i];
- break;
- }
- }
- if (!defined(layer)) {
- throw new RuntimeError('Unable to find first child of the feature info xml document');
- }
- var featureMembers = layer.childNodes;
- for (var featureIndex = 0; featureIndex < featureMembers.length; ++featureIndex) {
- var featureMember = featureMembers[featureIndex];
- if (featureMember.nodeType === Node.ELEMENT_NODE) {
- var properties = {};
- getGmlPropertiesRecursively(featureMember, properties);
- result.push(imageryLayerFeatureInfoFromDataAndProperties(featureMember, properties));
- }
- }
- return result;
- }
- function getGmlPropertiesRecursively(gmlNode, properties) {
- var isSingleValue = true;
- for (var i = 0; i < gmlNode.childNodes.length; ++i) {
- var child = gmlNode.childNodes[i];
- if (child.nodeType === Node.ELEMENT_NODE) {
- isSingleValue = false;
- }
- if (child.localName === 'Point' || child.localName === 'LineString' || child.localName === 'Polygon' || child.localName === 'boundedBy') {
- continue;
- }
- if (child.hasChildNodes() && getGmlPropertiesRecursively(child, properties)) {
- properties[child.localName] = child.textContent;
- }
- }
- return isSingleValue;
- }
- function imageryLayerFeatureInfoFromDataAndProperties(data, properties) {
- var featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = data;
- featureInfo.properties = properties;
- featureInfo.configureNameFromProperties(properties);
- featureInfo.configureDescriptionFromProperties(properties);
- return featureInfo;
- }
- function unknownXmlToFeatureInfo(xml) {
- var xmlText = new XMLSerializer().serializeToString(xml);
- var element = document.createElement('div');
- var pre = document.createElement('pre');
- pre.textContent = xmlText;
- element.appendChild(pre);
- var featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = xml;
- featureInfo.description = element.innerHTML;
- return [featureInfo];
- }
- var emptyBodyRegex= /<body>\s*<\/body>/im;
- var wmsServiceExceptionReportRegex = /<ServiceExceptionReport([\s\S]*)<\/ServiceExceptionReport>/im;
- var titleRegex = /<title>([\s\S]*)<\/title>/im;
- function textToFeatureInfo(text) {
- // If the text is HTML and it has an empty body tag, assume it means no features were found.
- if (emptyBodyRegex.test(text)) {
- return undefined;
- }
- // If this is a WMS exception report, treat it as "no features found" rather than showing
- // bogus feature info.
- if (wmsServiceExceptionReportRegex.test(text)) {
- return undefined;
- }
- // If the text has a <title> element, use it as the name.
- var name;
- var title = titleRegex.exec(text);
- if (title && title.length > 1) {
- name = title[1];
- }
- var featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.name = name;
- featureInfo.description = text;
- featureInfo.data = text;
- return [featureInfo];
- }
- export default GetFeatureInfoFormat;
|