123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- 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) {
- const result = [];
- const features = json.features;
- for (let i = 0; i < features.length; ++i) {
- const feature = features[i];
- const 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") {
- const longitude = feature.geometry.coordinates[0];
- const latitude = feature.geometry.coordinates[1];
- featureInfo.position = Cartographic.fromDegrees(longitude, latitude);
- }
- result.push(featureInfo);
- }
- return result;
- }
- const mapInfoMxpNamespace = "http://www.mapinfo.com/mxp";
- const esriWmsNamespace = "http://www.esri.com/wms";
- const wfsNamespace = "http://www.opengis.net/wfs";
- const gmlNamespace = "http://www.opengis.net/gml";
- function xmlToFeatureInfo(xml) {
- const 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) {
- const result = [];
- const multiFeatureCollection = xml.documentElement;
- const features = multiFeatureCollection.getElementsByTagNameNS(
- mapInfoMxpNamespace,
- "Feature"
- );
- for (let featureIndex = 0; featureIndex < features.length; ++featureIndex) {
- const feature = features[featureIndex];
- const properties = {};
- const propertyElements = feature.getElementsByTagNameNS(
- mapInfoMxpNamespace,
- "Val"
- );
- for (
- let propertyIndex = 0;
- propertyIndex < propertyElements.length;
- ++propertyIndex
- ) {
- const propertyElement = propertyElements[propertyIndex];
- if (propertyElement.hasAttribute("ref")) {
- const name = propertyElement.getAttribute("ref");
- const value = propertyElement.textContent.trim();
- properties[name] = value;
- }
- }
- const featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = feature;
- featureInfo.properties = properties;
- featureInfo.configureNameFromProperties(properties);
- featureInfo.configureDescriptionFromProperties(properties);
- result.push(featureInfo);
- }
- return result;
- }
- function esriXmlToFeatureInfo(xml) {
- const featureInfoResponse = xml.documentElement;
- const result = [];
- let properties;
- const features = featureInfoResponse.getElementsByTagNameNS("*", "FIELDS");
- if (features.length > 0) {
- // Standard esri format
- for (let featureIndex = 0; featureIndex < features.length; ++featureIndex) {
- const feature = features[featureIndex];
- properties = {};
- const propertyAttributes = feature.attributes;
- for (
- let attributeIndex = 0;
- attributeIndex < propertyAttributes.length;
- ++attributeIndex
- ) {
- const 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
- const featureInfoElements = featureInfoResponse.getElementsByTagNameNS(
- "*",
- "FeatureInfo"
- );
- for (
- let featureInfoElementIndex = 0;
- featureInfoElementIndex < featureInfoElements.length;
- ++featureInfoElementIndex
- ) {
- const featureInfoElement = featureInfoElements[featureInfoElementIndex];
- properties = {};
- // node.children is not supported in IE9-11, so use childNodes and check that child.nodeType is an element
- const featureInfoChildren = featureInfoElement.childNodes;
- for (
- let childIndex = 0;
- childIndex < featureInfoChildren.length;
- ++childIndex
- ) {
- const child = featureInfoChildren[childIndex];
- if (child.nodeType === Node.ELEMENT_NODE) {
- properties[child.localName] = child.textContent;
- }
- }
- result.push(
- imageryLayerFeatureInfoFromDataAndProperties(
- featureInfoElement,
- properties
- )
- );
- }
- }
- return result;
- }
- function gmlToFeatureInfo(xml) {
- const result = [];
- const featureCollection = xml.documentElement;
- const featureMembers = featureCollection.getElementsByTagNameNS(
- gmlNamespace,
- "featureMember"
- );
- for (
- let featureIndex = 0;
- featureIndex < featureMembers.length;
- ++featureIndex
- ) {
- const featureMember = featureMembers[featureIndex];
- const 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) {
- const result = [];
- // Find the first child. Except for IE, this would work:
- // const layer = xml.documentElement.children[0];
- let layer;
- const children = xml.documentElement.childNodes;
- for (let 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"
- );
- }
- const featureMembers = layer.childNodes;
- for (
- let featureIndex = 0;
- featureIndex < featureMembers.length;
- ++featureIndex
- ) {
- const featureMember = featureMembers[featureIndex];
- if (featureMember.nodeType === Node.ELEMENT_NODE) {
- const properties = {};
- getGmlPropertiesRecursively(featureMember, properties);
- result.push(
- imageryLayerFeatureInfoFromDataAndProperties(featureMember, properties)
- );
- }
- }
- return result;
- }
- function getGmlPropertiesRecursively(gmlNode, properties) {
- let isSingleValue = true;
- for (let i = 0; i < gmlNode.childNodes.length; ++i) {
- const 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) {
- const featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = data;
- featureInfo.properties = properties;
- featureInfo.configureNameFromProperties(properties);
- featureInfo.configureDescriptionFromProperties(properties);
- return featureInfo;
- }
- function unknownXmlToFeatureInfo(xml) {
- const xmlText = new XMLSerializer().serializeToString(xml);
- const element = document.createElement("div");
- const pre = document.createElement("pre");
- pre.textContent = xmlText;
- element.appendChild(pre);
- const featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.data = xml;
- featureInfo.description = element.innerHTML;
- return [featureInfo];
- }
- const emptyBodyRegex = /<body>\s*<\/body>/im;
- const wmsServiceExceptionReportRegex = /<ServiceExceptionReport([\s\S]*)<\/ServiceExceptionReport>/im;
- const 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.
- let name;
- const title = titleRegex.exec(text);
- if (title && title.length > 1) {
- name = title[1];
- }
- const featureInfo = new ImageryLayerFeatureInfo();
- featureInfo.name = name;
- featureInfo.description = text;
- featureInfo.data = text;
- return [featureInfo];
- }
- export default GetFeatureInfoFormat;
|