You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
396 lines
12 KiB
JavaScript
396 lines
12 KiB
JavaScript
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;
|