"""! @brief Parsers for WMS/WMTS/NASA OnEarth capabilities files. List of classes: - wms_cap_parsers::BaseCapabilitiesTree - wms_cap_parsers::WMSXMLNsHandler - wms_cap_parsers::WMSCapabilitiesTree - wms_cap_parsers::WMTSXMLNsHandler - wms_cap_parsers::WMTSCapabilitiesTree - wms_cap_parsers::OnEarthCapabilitiesTree (C) 2012 by the GRASS Development Team This program is free software under the GNU General Public License (>=v2). Read the file COPYING that comes with GRASS for details. @author Stepan Turek (Mentor: Martin Landa) """ try: from xml.etree.ElementTree import ParseError except ImportError: # < Python 2.7 from xml.parsers.expat import ExpatError as ParseError import xml.etree.ElementTree as etree import grass.script as grass # i18N import os import gettext gettext.install('grassmods', os.path.join(os.getenv("GISBASE"), 'locale')) class BaseCapabilitiesTree(etree.ElementTree): def __init__(self, cap_file): """!Initialize xml.etree.ElementTree """ try: etree.ElementTree.__init__(self, file=cap_file) except ParseError: raise ParseError(_("Unable to parse XML file")) except IOError as error: raise ParseError(_("Unable to open XML file '%s'.\n%s\n" % (cap_file, error))) if self.getroot() is None: raise ParseError(_("Root node was not found.")) class WMSXMLNsHandler: def __init__(self, caps): """!Handle XML namespaces according to WMS version of capabilities. """ self.namespace = "{http://www.opengis.net/wms}" if caps.getroot().find("Service") is not None: self.use_ns = False elif caps.getroot().find(self.namespace + "Service") is not None: self.use_ns = True else: raise ParseError(_("Unable to parse capabilities file.\n\ Tag <%s> was not found.") % "Service") def Ns(self, tag_name): """!Add namespace to tag_name according to version """ if self.use_ns: tag_name = self.namespace + tag_name return tag_name class WMSCapabilitiesTree(BaseCapabilitiesTree): def __init__(self, cap_file, force_version=None): """!Parses WMS capabilities file. If the capabilities file cannot be parsed if it raises xml.etree.ElementTree.ParseError. The class manges inheritance in 'Layer' elements. Inherited elements are added to 'Layer' element. The class also removes elements which are in invalid form and are needed by wxGUI capabilities dialog. @param cap_file - capabilities file @param force_version - force capabilities file version (1.1.1, 1.3.0) """ BaseCapabilitiesTree.__init__(self, cap_file) self.xml_ns = WMSXMLNsHandler(self) grass.debug('Checking WMS capabilities tree.', 4) if not "version" in self.getroot().attrib: raise ParseError(_("Missing version attribute root node " "in Capabilities XML file")) else: wms_version = self.getroot().attrib["version"] if wms_version == "1.3.0": self.proj_tag = "CRS" else: self.proj_tag = "SRS" if force_version is not None: if wms_version != force_version: raise ParseError(_("WMS server does not support '%s' version.") % wms_version) capability = self._find(self.getroot(), "Capability") root_layer = self._find(capability, "Layer") self._checkFormats(capability) self._checkLayerTree(root_layer) grass.debug('Check of WMS capabilities tree was finished.', 4) def _checkFormats(self, capability): """!Check if format element is defined. """ request = self._find(capability, "Request") get_map = self._find(request, "GetMap") formats = self._findall(get_map, "Format") def _checkLayerTree(self, parent_layer, first=True): """!Recursively check layer tree and manage inheritance in the tree """ if first: self._initLayer(parent_layer, None) layers = parent_layer.findall((self.xml_ns.Ns("Layer"))) for l in layers: self._initLayer(l, parent_layer) self._checkLayerTree(l, False) def _initLayer(self, layer, parent_layer): """Inherit elements from parent layer @param layer - element which inherits @param parent_layer - element which is inherited from """ if parent_layer is not None: replaced_elements = [["EX_GeographicBoundingBox", "replace"], ["Attribution", "replace"], ["MinScaleDenominator", "replace"], ["MaxScaleDenominator", "replace"], ["AuthorityURL", "add"]] for element in replaced_elements: elems = layer.findall(self.xml_ns.Ns(element[0])) if len(elems) != 0 or element[1] == "add": for e in parent_layer.findall(self.xml_ns.Ns(element[0])): layer.append(e) inh_arguments = ["queryable", "cascaded", "opaque", "noSubsets", "fixedWidth", "fixedHeight"] for attr in parent_layer.attrib: if attr not in layer.attrib and attr in inh_arguments: layer.attrib[attr] = parent_layer.attrib[attr] self._inhNotSame(self.proj_tag, "element_content", layer, parent_layer) self._inhNotSame("BoundingBox", "attribute", layer, parent_layer, self.proj_tag) # remove invalid Styles styles = layer.findall(self.xml_ns.Ns('Style')) for s in styles: s_name = s.find(self.xml_ns.Ns('Name')) if s_name is None or not s_name.text: grass.debug('Removed invalid