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.