service/OGC/WMSCapabilities.js Source
import {
    Zondy
} from '../common/Base';
import {
    readHref
} from './util/XLink';
import XML from './util/XML';
import {
    readDecimalString,
    readString,
    readNonNegativeInteger,
    readDecimal,
    readBooleanString,
    readNonNegativeIntegerString
} from './util/xsd';
import {
    makeArrayPusher,
    makeObjectPropertyPusher,
    makeObjectPropertySetter,
    makeStructureNS,
    pushParseAndPop
} from './util/base/xml';

/**
 * @private
 * @const NAMESPACE_URIS 命名空间标识符
 * @type {Array<null|string>}
 */
const NAMESPACE_URIS = [
    null,
    'http://www.opengis.net/wms'
];

/**
 * @private
 * @const PARSERS
 * @type {Object<string, Object<string>>}
 */
const PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Service': makeObjectPropertySetter(readService),
        'Capability': makeObjectPropertySetter(readCapability)
    });

/**
 * @private
 * @const CAPABILITY_PARSERS
 * @type {Object<string, Object<string>>}
 */
const CAPABILITY_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Request': makeObjectPropertySetter(readRequest),
        'Exception': makeObjectPropertySetter(readException),
        'Layer': makeObjectPropertySetter(readCapabilityLayer)
    });

/**
 * @author 基础平台/产品2部 龚跃健
 * @class Zondy.Format.WMSCapabilities
 * @classdesc  用于读取WMS capabilities数据的类
 * @extends  Zondy.Format.XML
 * @example
 * var parser = new Zondy.Format.WMSCapabilities();
 * var result = parser.read(doc);
 */
class WMSCapabilities extends XML {
    constructor() {
        super();

        /**
         * @private
         * @member Zondy.Format.WMSCapabilities.prototype.version
         * @type {string|undefined}
         * @description 版本号
         * @default undefined
         */
        this.version = undefined;
    }

    /**
     * @private
     * @function Zondy.Format.WMSCapabilities.prototype.readFromDocument
     * @description 从文档中读取信息,继承于父类XML
     * @return {wmsCapabilityObject|null}
     */
    readFromDocument(doc) {
        for (let n = doc.firstChild; n; n = n.nextSibling) {
            if (n.nodeType == Node.ELEMENT_NODE) {
                return this.readFromNode(n);
            }
        }
        return null;
    }

    /**
     * @private
     * @function Zondy.Format.WMSCapabilities.prototype.readFromNode
     * @description 从文档节点中读取信息,继承于父类XML
     * @return {wmsCapabilityObject|null}
     */
    readFromNode(node) {
        this.version = node.getAttribute('version').trim();
        const wmsCapabilityObject = pushParseAndPop({
            'version': this.version
        }, PARSERS, node, []);
        return wmsCapabilityObject ? wmsCapabilityObject : null;
    }
}

/**
 * @private
 * @const SERVICE_PARSERS
 * @type {Object<string, Object<string>>}
 */
const SERVICE_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Name': makeObjectPropertySetter(readString),
        'Title': makeObjectPropertySetter(readString),
        'Abstract': makeObjectPropertySetter(readString),
        'KeywordList': makeObjectPropertySetter(readKeywordList),
        'OnlineResource': makeObjectPropertySetter(readHref),
        'ContactInformation': makeObjectPropertySetter(readContactInformation),
        'Fees': makeObjectPropertySetter(readString),
        'AccessConstraints': makeObjectPropertySetter(readString),
        'LayerLimit': makeObjectPropertySetter(readNonNegativeInteger),
        'MaxWidth': makeObjectPropertySetter(readNonNegativeInteger),
        'MaxHeight': makeObjectPropertySetter(readNonNegativeInteger)
    });

/**
 * @private
 * @const CONTACT_INFORMATION_PARSERS
 * @type {Object<string, Object<string>>}
 */
const CONTACT_INFORMATION_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'ContactPersonPrimary': makeObjectPropertySetter(readContactPersonPrimary),
        'ContactPosition': makeObjectPropertySetter(readString),
        'ContactAddress': makeObjectPropertySetter(readContactAddress),
        'ContactVoiceTelephone': makeObjectPropertySetter(readString),
        'ContactFacsimileTelephone': makeObjectPropertySetter(readString),
        'ContactElectronicMailAddress': makeObjectPropertySetter(readString)
    });

/**
 * @private
 * @const CONTACT_PERSON_PARSERS
 * @type {Object<string, Object<string>>}
 */
const CONTACT_PERSON_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'ContactPerson': makeObjectPropertySetter(readString),
        'ContactOrganization': makeObjectPropertySetter(readString)
    });

/**
 * @private
 * @const CONTACT_ADDRESS_PARSERS
 * @type {Object<string, Object<string>>}
 */
const CONTACT_ADDRESS_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'AddressType': makeObjectPropertySetter(readString),
        'Address': makeObjectPropertySetter(readString),
        'City': makeObjectPropertySetter(readString),
        'StateOrProvince': makeObjectPropertySetter(readString),
        'PostCode': makeObjectPropertySetter(readString),
        'Country': makeObjectPropertySetter(readString)
    });

/**
 * @private
 * @const EXCEPTION_PARSERS
 * @type {Object<string, Object<string>>}
 */
const EXCEPTION_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Format': makeArrayPusher(readString)
    });

/**
 * @private
 * @const LAYER_PARSERS
 * @type {Object<string, Object<string>>}
 */
const LAYER_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Name': makeObjectPropertySetter(readString),
        'Title': makeObjectPropertySetter(readString),
        'Abstract': makeObjectPropertySetter(readString),
        'KeywordList': makeObjectPropertySetter(readKeywordList),
        'SRS': makeObjectPropertyPusher(readString),
        'EX_GeographicBoundingBox': makeObjectPropertySetter(readEXGeographicBoundingBox),
        'BoundingBox': makeObjectPropertyPusher(readBoundingBox),
        'Dimension': makeObjectPropertyPusher(readDimension),
        'Attribution': makeObjectPropertySetter(readAttribution),
        'AuthorityURL': makeObjectPropertyPusher(readAuthorityURL),
        'Identifier': makeObjectPropertyPusher(readString),
        'MetadataURL': makeObjectPropertyPusher(readMetadataURL),
        'DataURL': makeObjectPropertyPusher(readFormatOnlineresource),
        'FeatureListURL': makeObjectPropertyPusher(readFormatOnlineresource),
        'Style': makeObjectPropertyPusher(readStyle),
        'MinScaleDenominator': makeObjectPropertySetter(readDecimal),
        'MaxScaleDenominator': makeObjectPropertySetter(readDecimal),
        'Layer': makeObjectPropertyPusher(readLayer)
    });

/**
 * @private
 * @const ATTRIBUTION_PARSERS
 * @type {Object<string, Object<string>>}
 */
const ATTRIBUTION_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Title': makeObjectPropertySetter(readString),
        'OnlineResource': makeObjectPropertySetter(readHref),
        'LogoURL': makeObjectPropertySetter(readSizedFormatOnlineresource)
    });

/**
 * @private
 * @const EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS
 * @type {Object<string, Object<string>>}
 */
const EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS =
    makeStructureNS(NAMESPACE_URIS, {
        'westBoundLongitude': makeObjectPropertySetter(readDecimal),
        'eastBoundLongitude': makeObjectPropertySetter(readDecimal),
        'southBoundLatitude': makeObjectPropertySetter(readDecimal),
        'northBoundLatitude': makeObjectPropertySetter(readDecimal)
    });

/**
 * @private
 * @const REQUEST_PARSERS
 * @type {Object<string, Object<string>>}
 */
const REQUEST_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'GetCapabilities': makeObjectPropertySetter(readOperationType),
        'GetMap': makeObjectPropertySetter(readOperationType),
        'GetFeatureInfo': makeObjectPropertySetter(readOperationType)
    });

/**
 * @private
 * @const OPERATIONTYPE_PARSERS
 * @type {Object<string, Object<string>>}
 */
const OPERATIONTYPE_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Format': makeObjectPropertyPusher(readString),
        'DCPType': makeObjectPropertyPusher(readDCPType)
    });

/**
 * @private
 * @const DCPTYPE_PARSERS
 * @type {Object<string, Object<string>>}
 */
const DCPTYPE_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'HTTP': makeObjectPropertySetter(readHTTP)
    });

/**
 * @private
 * @const HTTP_PARSERS
 * @type {Object<string, Object<string>>}
 */
const HTTP_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Get': makeObjectPropertySetter(readFormatOnlineresource),
        'Post': makeObjectPropertySetter(readFormatOnlineresource)
    });

/**
 * @private
 * @const STYLE_PARSERS
 * @type {Object<string, Object<string>>}
 */
const STYLE_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Name': makeObjectPropertySetter(readString),
        'Title': makeObjectPropertySetter(readString),
        'Abstract': makeObjectPropertySetter(readString),
        'LegendURL': makeObjectPropertyPusher(readSizedFormatOnlineresource),
        'StyleSheetURL': makeObjectPropertySetter(readFormatOnlineresource),
        'StyleURL': makeObjectPropertySetter(readFormatOnlineresource)
    });

/**
 * @private
 * @const FORMAT_ONLINERESOURCE_PARSERS
 * @type {Object<string, Object<string>>}
 */
const FORMAT_ONLINERESOURCE_PARSERS =
    makeStructureNS(NAMESPACE_URIS, {
        'Format': makeObjectPropertySetter(readString),
        'OnlineResource': makeObjectPropertySetter(readHref)
    });

/**
 * @private
 * @const KEYWORDLIST_PARSERS
 * @type {Object<string, Object<string>>}
 */
const KEYWORDLIST_PARSERS = makeStructureNS(
    NAMESPACE_URIS, {
        'Keyword': makeArrayPusher(readString)
    });

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readAttribution
 * @description 读取属性信息
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} 属性对象.
 */
function readAttribution(node, objectStack) {
    return pushParseAndPop({}, ATTRIBUTION_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readBoundingBox
 * @description 读取地图坐标系信息,包括地理坐标系名,范围,分辨率
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object} 地图范围对象.
 */
function readBoundingBox(node, objectStack) {
    const extent = [
        readDecimalString(node.getAttribute('minx')),
        readDecimalString(node.getAttribute('miny')),
        readDecimalString(node.getAttribute('maxx')),
        readDecimalString(node.getAttribute('maxy'))
    ];

    const resolutions = [
        readDecimalString(node.getAttribute('resx')),
        readDecimalString(node.getAttribute('resy'))
    ];

    return {
        'srs': node.getAttribute('SRS'),
        'extent': extent,
        'res': resolutions
    };
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readEXGeographicBoundingBox
 * @description 读取地理坐标范围
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object} 地理坐标范围对象.
 */
function readEXGeographicBoundingBox(node, objectStack) {
    const geographicBoundingBox = pushParseAndPop({},
        EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS,
        node, objectStack);
    if (!geographicBoundingBox) {
        return undefined;
    }
    const westBoundLongitude = geographicBoundingBox['westBoundLongitude'];
    const southBoundLatitude = geographicBoundingBox['southBoundLatitude'];
    const eastBoundLongitude = geographicBoundingBox['eastBoundLongitude'];
    const northBoundLatitude = geographicBoundingBox['northBoundLatitude'];
    if (westBoundLongitude === undefined || southBoundLatitude === undefined ||
        eastBoundLongitude === undefined || northBoundLatitude === undefined) {
        return undefined;
    }
    return [
        westBoundLongitude, southBoundLatitude,
        eastBoundLongitude, northBoundLatitude
    ];
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readCapability
 * @description 读取Capability
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Capability对象.
 */
function readCapability(node, objectStack) {
    return pushParseAndPop({}, CAPABILITY_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readService
 * @description 读取Service
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Service对象.
 */
function readService(node, objectStack) {
    return pushParseAndPop({}, SERVICE_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readContactInformation
 * @description 读取ContactInformation
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Contact information 对象.
 */
function readContactInformation(node, objectStack) {
    return pushParseAndPop({}, CONTACT_INFORMATION_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readContactPersonPrimary
 * @description 读取ContactPersonPrimary
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Contact person 对象.
 */
function readContactPersonPrimary(node, objectStack) {
    return pushParseAndPop({}, CONTACT_PERSON_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readContactAddress
 * @description 读取ContactAddress
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Contact address对象.
 */
function readContactAddress(node, objectStack) {
    return pushParseAndPop({}, CONTACT_ADDRESS_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readException
 * @description 读取Exception
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Array<string>|undefined} Format array.
 */
function readException(node, objectStack) {
    return pushParseAndPop([], EXCEPTION_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readCapabilityLayer
 * @description 读取CapabilityLayer
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Layer对象.
 */
function readCapabilityLayer(node, objectStack) {
    return pushParseAndPop({}, LAYER_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readLayer
 * @description 读取Layer
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Layer对象.
 */
function readLayer(node, objectStack) {
    const parentLayerObject = (objectStack[objectStack.length - 1]);

    const layerObject = pushParseAndPop({}, LAYER_PARSERS, node, objectStack);

    if (!layerObject) {
        return undefined;
    }
    let queryable = readBooleanString(node.getAttribute('queryable'));
    if (queryable === undefined) {
        queryable = parentLayerObject['queryable'];
    }
    layerObject['queryable'] = queryable !== undefined ? queryable : false;

    let cascaded = readNonNegativeIntegerString(
        node.getAttribute('cascaded'));
    if (cascaded === undefined) {
        cascaded = parentLayerObject['cascaded'];
    }
    layerObject['cascaded'] = cascaded;

    let opaque = readBooleanString(node.getAttribute('opaque'));
    if (opaque === undefined) {
        opaque = parentLayerObject['opaque'];
    }
    layerObject['opaque'] = opaque !== undefined ? opaque : false;

    let noSubsets = readBooleanString(node.getAttribute('noSubsets'));
    if (noSubsets === undefined) {
        noSubsets = parentLayerObject['noSubsets'];
    }
    layerObject['noSubsets'] = noSubsets !== undefined ? noSubsets : false;

    let fixedWidth = readDecimalString(node.getAttribute('fixedWidth'));
    if (!fixedWidth) {
        fixedWidth = parentLayerObject['fixedWidth'];
    }
    layerObject['fixedWidth'] = fixedWidth;

    let fixedHeight = readDecimalString(node.getAttribute('fixedHeight'));
    if (!fixedHeight) {
        fixedHeight = parentLayerObject['fixedHeight'];
    }
    layerObject['fixedHeight'] = fixedHeight;

    // See 7.2.4.8
    const addKeys = ['Style', 'SRS', 'AuthorityURL'];
    addKeys.forEach(function (key) {
        if (key in parentLayerObject) {
            const childValue = layerObject[key] || [];
            layerObject[key] = childValue.concat(parentLayerObject[key]);
        }
    });

    const replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension',
        'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator'
    ];
    replaceKeys.forEach(function (key) {
        if (!(key in layerObject)) {
            const parentValue = parentLayerObject[key];
            layerObject[key] = parentValue;
        }
    });

    return layerObject;
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readDimension
 * @description 读取Dimension
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object} Dimension对象.
 */
function readDimension(node, objectStack) {
    const dimensionObject = {
        'name': node.getAttribute('name'),
        'units': node.getAttribute('units'),
        'unitSymbol': node.getAttribute('unitSymbol'),
        'default': node.getAttribute('default'),
        'multipleValues': readBooleanString(node.getAttribute('multipleValues')),
        'nearestValue': readBooleanString(node.getAttribute('nearestValue')),
        'current': readBooleanString(node.getAttribute('current')),
        'values': readString(node)
    };
    return dimensionObject;
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readFormatOnlineresource
 * @description 读取FormatOnlineresource
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Online resource对象.
 */
function readFormatOnlineresource(node, objectStack) {
    return pushParseAndPop({}, FORMAT_ONLINERESOURCE_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readRequest
 * @description 读取Request
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Request对象.
 */
function readRequest(node, objectStack) {
    return pushParseAndPop({}, REQUEST_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readDCPType
 * @description 读取DCPType
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} DCP type对象.
 */
function readDCPType(node, objectStack) {
    return pushParseAndPop({}, DCPTYPE_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readHTTP
 * @description 读取HTTP
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} HTTP对象.
 */
function readHTTP(node, objectStack) {
    return pushParseAndPop({}, HTTP_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readOperationType
 * @description 读取OperationType
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Operation type对象.
 */
function readOperationType(node, objectStack) {
    return pushParseAndPop({}, OPERATIONTYPE_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readSizedFormatOnlineresource
 * @description 读取SizedFormatOnlineresource
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Online resource对象.
 */
function readSizedFormatOnlineresource(node, objectStack) {
    const formatOnlineresource = readFormatOnlineresource(node, objectStack);
    if (formatOnlineresource) {
        const size = [
            readNonNegativeIntegerString(node.getAttribute('width')),
            readNonNegativeIntegerString(node.getAttribute('height'))
        ];
        formatOnlineresource['size'] = size;
        return formatOnlineresource;
    }
    return undefined;
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readAuthorityURL
 * @description 读取AuthorityURL
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Authority URL对象.
 */
function readAuthorityURL(node, objectStack) {
    const authorityObject = readFormatOnlineresource(node, objectStack);
    if (authorityObject) {
        authorityObject['name'] = node.getAttribute('name');
        return authorityObject;
    }
    return undefined;
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readMetadataURL
 * @description 读取MetadataURL
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Metadata URL对象.
 */
function readMetadataURL(node, objectStack) {
    const metadataObject = readFormatOnlineresource(node, objectStack);
    if (metadataObject) {
        metadataObject['type'] = node.getAttribute('type');
        return metadataObject;
    }
    return undefined;
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readStyle
 * @description 读取Style
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Object|undefined} Style对象.
 */
function readStyle(node, objectStack) {
    return pushParseAndPop({}, STYLE_PARSERS, node, objectStack);
}

/**
 * @private
 * @function Zondy.Format.WMSCapabilities.prototype.readKeywordList
 * @description 读取KeywordList
 * @param {Element} node 节点.
 * @param {Array<*>} objectStack 对象堆栈.
 * @return {Array<string>|undefined} Keyword list.
 */
function readKeywordList(node, objectStack) {
    return pushParseAndPop([], KEYWORDLIST_PARSERS, node, objectStack);
}

export {
    WMSCapabilities
};
Zondy.Format.WMSCapabilities = WMSCapabilities;