/**
* @module core/parse
*/
'use strict';
var utils = require("./utils");
var parseXml;
if (typeof window.DOMParser != "undefined") {
parseXml = function(xmlStr) {
return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml");
};
} else if (typeof window.ActiveXObject != "undefined" &&
new window.ActiveXObject("Microsoft.XMLDOM")) {
parseXml = function(xmlStr) {
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
}
/**
* @private
* @global
*/
/**
* A hash-table associating the node name of common WCS objects with their
* according parse function.
*/
var parseFunctions = {};
/**
* @private
*/
var ns = {
xlink: "http://www.w3.org/1999/xlink",
ows: "http://www.opengis.net/ows/2.0",
wcs: "http://www.opengis.net/wcs/2.0",
gml: "http://www.opengis.net/gml/3.2",
gmlcov: "http://www.opengis.net/gmlcov/1.0",
swe: "http://www.opengis.net/swe/2.0",
crs: "http://www.opengis.net/wcs/crs/1.0",
int: "http://www.opengis.net/wcs/interpolation/1.0"
}
var xPath = utils.createXPath(ns);
var xPathArray = utils.createXPathArray(ns);
/**
* Registers a new node parsing function for a specified tagName. A function
* can be registered to multiple tagNames.
*
* @param tagName the tagName the function is registered to
*
* @param parseFunction the function to be executed. The function shall
* receive the tag name and a wrapped DOM object
* as parameters and shall return an object of all parsed
* attributes. For extension parsing functions only
* extensive properties shall be parsed.
*/
function pushParseFunction(tagName, parseFunction) {
if (parseFunctions.hasOwnProperty(tagName)) {
parseFunctions[tagName].push(parseFunction);
}
else {
parseFunctions[tagName] = [parseFunction];
}
}
/**
* Convenience function to push multiple parsing functions at one. The same
* rules as with `WCS.Core.pushParseFunction` apply here.
*
* @param obj a hash-table with key-value pairs, where the key is the tag name
* and the value the parsing function.
*/
function pushParseFunctions(obj) {
for (var key in obj) {
pushParseFunction(key, obj[key]);
}
}
/**
* Calls all registered functions for a specified node name. A merged object
* with all results of each function is returned.
*
* @param tagName the tagName of the node to be parsed
*
* @param node the DOM object
*
* @returns the merged object of all parsing results
*/
function callParseFunctions(tagName, node, options) {
if (parseFunctions.hasOwnProperty(tagName)) {
var funcs = parseFunctions[tagName],
endResult = {};
for (var i = 0; i < funcs.length; ++i) {
var result = funcs[i](node, options);
utils.deepMerge(endResult, result);
}
return endResult;
}
else
throw new Error("No parsing function for tag name '" + tagName + "' registered.");
}
/**
* Parses a (EO-)WCS response to JavaScript objects.
*
* @param xml the XML string to be parsed
* @param options options for parsing
* @param options.throwOnException if true, an exception is thrown when an
* exception report is parsed
*
* @returns depending on the response a JavaScript object with all parsed data
* or a collection thereof.
*/
function parse(xml, options) {
var root;
if (typeof xml === "string") {
root = parseXml(xml).documentElement;
}
else {
root = xml.documentElement;
}
return callParseFunctions(root.localName, root, options);
}
/**
* Parsing function for ows:ExceptionReport elements.
*
* @param node the DOM object
* @param options
* @param options.throwOnException throw an exception when an exception report
* is parsed
*
* @returns the parsed object
*/
function parseExceptionReport(node, options) {
var exception = xPath(node, "ows:Exception");
var parsed = {
"code": exception.getAttribute("exceptionCode"),
"locator": exception.getAttribute("locator"),
"text": xPath(exception, "ows:ExceptionText/text()")
};
if (options && options.throwOnException) {
var e = new Exception(parsed.text);
e.locator = parsed.locator;
e.code = parsed.code;
throw e;
}
else return parsed;
}
/**
* Parsing function for wcs:Capabilities elements.
*
* @param node the DOM object
*
* @returns the parsed object
*/
function parseCapabilities(node) {
return {
"serviceIdentification": {
"title": xPath(node, "ows:ServiceIdentification/ows:Title/text()"),
"abstract": xPath(node, "ows:ServiceIdentification/ows:Abstract/text()"),
"keywords": xPathArray(node, "ows:ServiceIdentification/ows:Keywords/ows:Keyword/text()"),
"serviceType": xPath(node, "ows:ServiceIdentification/ows:ServiceType/text()"),
"serviceTypeVersion": xPath(node, "ows:ServiceIdentification/ows:ServiceTypeVersion/text()"),
"profiles": xPathArray(node, "ows:ServiceIdentification/ows:Profile/text()"),
"fees": xPath(node, "ows:ServiceIdentification/ows:Fees/text()"),
"accessConstraints": xPath(node, "ows:ServiceIdentification/ows:AccessConstraints/text()")
},
"serviceProvider": {
"providerName": xPath(node, "ows:ServiceProvider/ows:ProviderName/text()"),
"providerSite": xPath(node, "ows:ServiceProvider/ows:ProviderSite/@xlink:href"),
"individualName": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:IndividualName/text()"),
"positionName": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:PositionName/text()"),
"contactInfo": {
"phone": {
"voice": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Phone/ows:Voice/text()"),
"facsimile": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Phone/ows:Facsimile/text()")
},
"address": {
"deliveryPoint": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:DeliveryPoint/text()"),
"city": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:City/text()"),
"administrativeArea": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:AdministrativeArea/text()"),
"postalCode": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:PostalCode/text()"),
"country": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:Country/text()"),
"electronicMailAddress": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:Address/ows:ElectronicMailAddress/text()")
},
"onlineResource": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:OnlineResource/@xlink:href"),
"hoursOfService": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:HoursOfService/text()"),
"contactInstructions": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:ContactInfo/ows:ContactInstructions/text()")
},
"role": xPath(node, "ows:ServiceProvider/ows:ServiceContact/ows:Role/text()")
},
"serviceMetadata": {
"formatsSupported": xPathArray(node, "wcs:ServiceMetadata/wcs:formatSupported/text()"),
"crssSupported": xPathArray(node, "wcs:ServiceMetadata/wcs:Extension/crs:CrsMetadata/crs:crsSupported/text()"),
"interpolationsSupported": xPathArray(node, "wcs:ServiceMetadata/wcs:Extension/int:InterpolationMetadata/int:InterpolationSupported/text()")
},
"operations": utils.map(xPathArray(node, "ows:OperationsMetadata/ows:Operation"), function(op) {
return {
"name": op.getAttribute("name"),
"getUrl": xPath(op, "ows:DCP/ows:HTTP/ows:Get/@xlink:href"),
"postUrl": xPath(op, "ows:DCP/ows:HTTP/ows:Post/@xlink:href")
};
}),
"contents": {
"coverages": utils.map(xPathArray(node, "wcs:Contents/wcs:CoverageSummary"), function(sum) {
return {
"coverageId": xPath(sum, "wcs:CoverageId/text()"),
"coverageSubtype": xPath(sum, "wcs:CoverageSubtype/text()")
};
})
}
};
}
/**
* Parsing function for wcs:CoverageDescriptions elements.
*
* @param node the DOM object
*
* @returns the parsed object
*/
function parseCoverageDescriptions(node) {
var descs = utils.map(xPathArray(node, "wcs:CoverageDescription"), function(desc) {
return callParseFunctions(desc.localName, desc);
});
return {"coverageDescriptions": descs};
}
/**
* Parsing function for wcs:CoverageDescription elements.
*
* @param node the DOM object
*
* @returns the parsed object
*/
function parseCoverageDescription(node) {
var low = utils.stringToIntArray(xPath(node, "gml:domainSet/gml:RectifiedGrid/gml:limits/gml:GridEnvelope/gml:low/text()|gml:domainSet/gml:ReferenceableGrid/gml:limits/gml:GridEnvelope/gml:low/text()")),
high = utils.stringToIntArray(xPath(node, "gml:domainSet/gml:RectifiedGrid/gml:limits/gml:GridEnvelope/gml:high/text()|gml:domainSet/gml:ReferenceableGrid/gml:limits/gml:GridEnvelope/gml:high/text()"));
var size = [];
for (var i = 0; i < Math.min(low.length, high.length); ++i) {
size.push(high[i] + 1 - low[i]);
}
var pos = xPath(node, "gml:domainSet/gml:RectifiedGrid/gml:origin/gml:Point/gml:pos/text()");
if (pos !== "") {
var origin = utils.stringToFloatArray(pos);
}
var offsetVectors = utils.map(xPathArray(node, "gml:domainSet/gml:RectifiedGrid/gml:offsetVector/text()"), function(offsetVector) {
return utils.stringToFloatArray(offsetVector);
});
// simplified resolution interface. does not make sense for not axis
// aligned offset vectors.
var resolution = [];
for (var i = 0; i < offsetVectors.length; ++i) {
for (var j = 0; j < offsetVectors.length; ++j) {
if (offsetVectors[j][i] != 0.0) {
resolution.push(offsetVectors[j][i]);
}
continue;
}
}
// get the grid, either rectified or referenceable
var grid = xPath(node, "gml:domainSet/gml:RectifiedGrid");
if (!grid) grid = xPath(node, "gml:domainSet/gml:ReferenceableGrid");
var obj = {
"coverageId": xPath(node, "wcs:CoverageId/text()"),
"dimensions": parseInt(xPath(node, "gml:domainSet/gml:RectifiedGrid/@dimension|gml:domainSet/gml:ReferenceableGrid/@dimension")),
"bounds": {
"projection": xPath(node, "gml:boundedBy/gml:Envelope/@srsName"),
"lower": utils.stringToFloatArray(xPath(node, "gml:boundedBy/gml:Envelope/gml:lowerCorner/text()")),
"upper": utils.stringToFloatArray(xPath(node, "gml:boundedBy/gml:Envelope/gml:upperCorner/text()"))
},
"envelope": {
"low": low,
"high": high
},
"size": size,
"origin": origin,
"offsetVectors": offsetVectors,
"resolution": resolution,
"rangeType": utils.map(xPathArray(node, "gmlcov:rangeType/swe:DataRecord/swe:field"), function(field) {
return {
"name": field.getAttribute("name"),
"description": xPath(field, "swe:Quantity/swe:description/text()"),
"uom": xPath(field, "swe:Quantity/swe:uom/@code"),
"nilValues": utils.map(xPathArray(field, "swe:Quantity/swe:nilValues/swe:NilValues/swe:nilValue"), function(nilValue) {
return {
"value": parseInt(nilValue.textContent),
"reason": nilValue.getAttribute("reason")
}
}),
"allowedValues": utils.stringToFloatArray(xPath(field, "swe:Quantity/swe:constraint/swe:AllowedValues/swe:interval/text()")),
"significantFigures": parseInt(xPath(field, "swe:Quantity/swe:constraint/swe:AllowedValues/swe:significantFigures/text()"))
};
}),
"coverageSubtype": xPath(node, "wcs:ServiceParameters/wcs:CoverageSubtype/text()"),
"nativeFormat": xPath(node, "wcs:ServiceParameters/wcs:nativeFormat/text()")
};
return obj;
}
/* Push core parsing functions */
pushParseFunctions({
"Capabilities": parseCapabilities,
"ExceptionReport": parseExceptionReport,
"CoverageDescriptions": parseCoverageDescriptions,
"CoverageDescription": parseCoverageDescription,
"RectifiedGridCoverage": parseCoverageDescription
});
module.exports = {
pushParseFunction: pushParseFunction,
pushParseFunctions: pushParseFunctions,
parse: parse,
callParseFunctions: callParseFunctions
}