Search code examples
javascriptarraysxmldomwms

Using a Javascript DOM Parser extract the list of Layers from the XML response.data of an WMS GetCapabilities request


I am trying to extract the list of the names of available layers of a WMS server. I have done so for the GeoMet WMS by sending a GetCapabilities which returns a "application/xml" object that then I parse using a DOM parser. My problem is the Layer tags are nested on two levels. Basically the top level layer contains multiple children layers. How would I extract only the children or list of the parent Layers. I managed to hack together this by realising the children had an attribute that the parent Node did not, but there has to be a better way.

EDIT : I am interested in getting the full list of layers that can be added to an interactive map. Basically all Layer tags that do not have Layer children.

axios.get('https://geo.weather.gc.ca/geomet?lang=en&service=WMS&version=1.3.0&request=GetCapabilities').then((response) => {
        // console.log(response.headers)
        const parser = new DOMParser()
        const dom = parser.parseFromString(response.data, 'application/xml')
        let layerGroups = dom.querySelectorAll('[cascaded="0"]')
        let layerNames = []
        layerGroups.forEach(function (domel) { layerNames.push(domel.getElementsByTagName('Name')[0].innerHTML) })
        console.log(layerNames.length)
        this.mylayerlist = layerNames
      })

Solution

  • There are a couple of ways to handle this, especially the namespaces. Here's one of them. After your dom declaration try this:

    xpath = '//*[local-name()="Layer"][@cascaded="0"][not(.//*[local-name()="Layer"])]/*[local-name()="Name"]';
    var targets = dom.evaluate(xpath, dom, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    let nodes = Array.from({ length: targets.snapshotLength }, (_, index) => 
    console.log(targets.snapshotItem(index).innerHTML));
    

    Output is 3,519 (in this case) of names:

    CGSL.ETA_ICEC
    CGSL.ETA_ICEPRS
    CGSL.ETA_ICESTG
    CGSL.ETA_ICET
    CGSL.ETA_ICETK
    

    The critical component here is the xpath expression. It selects all Layer nodes which have a cascaded attribute with a value of 0 and no Layer child nodes. In this particular case, none of the Layer nodes which have a cascaded attribute (note that all of these attributes have a 0 attribute value) have Layer children, so you could dispense with one of the two predicates (either [@cascaded="0"] or [not(./*[local-name()="Layer"])] and get the same output, though this may not be the case with other files).