def __init__(self, config): self.wmsCapabilityCache = WmsCapabilityCache(config) self.proxyUrl = config.get('proxyUrl')
class WmsCapabilityReader(): def __init__(self, config): self.wmsCapabilityCache = WmsCapabilityCache(config) self.proxyUrl = config.get('proxyUrl') def getEndpointServiceData(self, endpoint, forceRefresh): """Returns service data for an endpoint. """ log.debug("getEndpointData called for %s", endpoint['wmsurl']) wmsUrl = self.makeProxiedUrl(endpoint['wmsurl']) wmcDoc = self.wmsCapabilityCache.getWmsCapabilities(wmsUrl, forceRefresh) dom = xml.dom.minidom.parseString(wmcDoc) # Get the namespace URI for the document root element. ns = dom.documentElement.namespaceURI # Get the version the WMS server responded with. wmsVersion = dom.documentElement.getAttribute('version') service = xml_util.getSingleChildByNameNS(dom.documentElement, ns, 'Service') if service == None: return None title = xml_util.getSingleChildTextByNameNS(service, ns, 'Title') abstract = xml_util.getSingleChildTextByNameNS(service, ns, 'Abstract') # Break internal references to facilitiate garbage collection. dom.unlink() return { 'version': wmsVersion, 'title': title, 'abstract': abstract } def getLayers(self, endpoint, parentId, idMap, keywordData, forceRefresh): """Returns a list of 'endpoint_hierarchy_builder.Node's for the layers of an endpoint in a WMC document. Also, adds nodes to a map from node ID to node. """ log.debug("getLayers called for %s", endpoint['wmsurl']) log.debug(" keywordData: %s" % keywordData) wmsUrl = self.makeProxiedUrl(endpoint['wmsurl']) wmcDoc = self.wmsCapabilityCache.getWmsCapabilities(wmsUrl, True) dom = xml.dom.minidom.parseString(wmcDoc) # Get the namespace URI for the document root element. ns = dom.documentElement.namespaceURI # Get the version the WMS server responded with. wmsVersion = dom.documentElement.getAttribute('version') nodes = [] capability = xml_util.getSingleChildByNameNS(dom.documentElement, ns, 'Capability') if capability == None: return None getCapabilitiesUrlEl = xml_util.getSingleChildByPathNS(capability, [(ns, 'Request'), (ns, 'GetCapabilities'), (ns, 'DCPType'), (ns, 'HTTP'), (ns, 'Get'), (ns, 'OnlineResource')]) getCapabilitiesUrl = getCapabilitiesUrlEl.getAttributeNS(XLINK_URI, 'href') if not getCapabilitiesUrl: getCapabilitiesUrl = xml_util.getAttributeByLocalName(getCapabilitiesUrlEl, 'href') log.debug("GetCapabilities URL: %s", getCapabilitiesUrl) getCapabilitiesUrl = parseEndpointString(getCapabilitiesUrl, {'REQUEST':'GetCapabilities', 'SERVICE':'WMS'}) getFeatureInfoOnlineResourceEl = xml_util.getSingleChildByPathNS(capability, [(ns, 'Request'), (ns, 'GetFeatureInfo'), (ns, 'DCPType'), (ns, 'HTTP'), (ns, 'Get'), (ns, 'OnlineResource')]) if getFeatureInfoOnlineResourceEl: getFeatureInfoUrl = getFeatureInfoOnlineResourceEl.getAttributeNS(XLINK_URI, 'href') if not getFeatureInfoUrl: getFeatureInfoUrl = xml_util.getAttributeByLocalName(getFeatureInfoOnlineResourceEl, 'href') log.debug("GetFeatureInfo URL: %s", getFeatureInfoUrl) getFeatureInfoUrl = getFeatureInfoUrl.rstrip('?&') else: getFeatureInfoUrl = None getMapOnlineResourceEl = xml_util.getSingleChildByPathNS(capability, [(ns, 'Request'), (ns, 'GetMap'), (ns, 'DCPType'), (ns, 'HTTP'), (ns, 'Get'), (ns, 'OnlineResource')]) getMapUrl = getMapOnlineResourceEl.getAttributeNS(XLINK_URI, 'href') if not getMapUrl: getMapUrl = xml_util.getAttributeByLocalName(getMapOnlineResourceEl, 'href') log.debug("GetMap URL: %s", getMapUrl) getMapUrl = getMapUrl.rstrip('?&') getMapUrl = self.makeProxiedUrl(getMapUrl) commonData = { 'getCapabilitiesUrl': getCapabilitiesUrl, 'getFeatureInfoUrl': getFeatureInfoUrl, 'getMapUrl': getMapUrl, 'wmsVersion': wmsVersion } if 'wcsurl' in endpoint: commonData['getCoverageUrl'] = endpoint['wcsurl'] for layer in xml_util.getChildrenByNameNS(capability, ns, 'Layer'): self.handleLayer(ns, layer, nodes, endpoint, parentId, commonData, keywordData, idMap) # Break internal references to facilitate garbage collection. dom.unlink() return nodes def handleLayer(self, ns, layerEl, nodes, endpoint, parentId, commonData, antecedentKeywordData, idMap): """Processes a layer. Determines whether the layer has sublayers, in which case this method is called recursively, or if it is a leaf, in which case a node is added to the node list. """ subLayers = xml_util.getChildrenByNameNS(layerEl, ns, 'Layer') isLeaf = len(subLayers) == 0 layer = WmsLayer() layer.populateFromLayerElement(ns, layerEl, commonData) log.debug("Layer %s %s" % (layer.name, layer.title)) self.setLayerId(layer, parentId, isLeaf) # Merge all keyword data applicable to the layer. keywordData = self.getLayerDataFromEndpoint(endpoint, layer) if antecedentKeywordData: mergedKeywordData = antecedentKeywordData.copy() mergedKeywordData.update(keywordData) else: mergedKeywordData = keywordData log.debug("keywordData: %s" % mergedKeywordData) layer.generateDimensionDisplayValues(mergedKeywordData.get('dimension_format', None), mergedKeywordData.get('dimension_reverse', None)) treeInfo = self.makeTreeInfo(endpoint, layer, isLeaf, keywordData) # Look for sublayers - if there any any, call this method recursively to add them to the tree, # otherwise just add a leaf node. children = [] node = Node(layer.id, layer, children, treeInfo, keywordData) #idMap[layer.id] = node nodes.append(node) if isLeaf: log.debug("Found layer: title '%s' (ID=%s)", layer.title, layer.id) else: for lyr in subLayers: self.handleLayer(ns, lyr, children, endpoint, layer.id, commonData, antecedentKeywordData, idMap) def setLayerId(self, layer, parentId, isLeaf): """Constructs an id for the tree node. """ if layer.name: layerId = layer.name else: layerId = layer.title if isLeaf: prefix = EndpointHierarchyBuilder.KEY_PREFIX_LEAF_LAYER else: prefix = EndpointHierarchyBuilder.KEY_PREFIX_CONTAINER_LAYER layer.id = prefix + layerId + EndpointHierarchyBuilder.LAYER_ID_SEPARATOR + parentId def getLayerDataFromEndpoint(self, endpoint, layer): """Finds keyword data configured for matching layers of an endpoint and stores them in the layer object. """ if ((not layer.name) or (not 'layerSetData' in endpoint) or (not 'layers' in endpoint['layerSetData'])): return {} layers = endpoint['layerSetData']['layers'] # Find whether one of the sets of layer data for the endpoint matches the layer name. layerKeywordData = {} for k, l in layers.iteritems(): if 'name' in l: namePatt = l['name'].format(endpoint=endpoint['key']) + '\Z' log.debug("namePatt '%s'" % namePatt) if re.match(namePatt, layer.name): log.debug("Matches %s" % layer.name) layerKeywordData = l return layerKeywordData def makeTreeInfo(self, endpoint, layer, isLeaf, keywordData): """Empty implementation of method to populate additional information for layers in the layer tree. """ return {} def ensureEndpointCached(self, endpoint, forceRefresh=False): """Retrieves and caches a WMS capabilities document if not already cached or if forceRefresh=True. """ if 'wmsurl' in endpoint: log.debug("ensureEndpointCached called for %s", endpoint['wmsurl']) wmsUrl = self.makeProxiedUrl(endpoint['wmsurl']) wmcDoc = self.wmsCapabilityCache.getWmsCapabilities(wmsUrl, forceRefresh) else: log.error("No WMS URL configured for endpoint") def makeProxiedUrl(self, url): """Converts a URL into one using a proxy that accepts URLs of the form http://<proxy prefix>/<target scheme>/<target host>/<target port>/<target path> """ if self.proxyUrl: urlParts = urlparse.urlsplit(url) netlocParts = urlParts.netloc.partition(':') host = netlocParts[0] port = netlocParts[2] if netlocParts[2] else '-' proxyParts = urlparse.urlsplit(self.proxyUrl) mergedPath = '/'.join([proxyParts.path.rstrip('/'), urlParts.scheme, host, port, urlParts.path.lstrip('/')]) mergedParts = (proxyParts.scheme, proxyParts.netloc, mergedPath, urlParts.query, urlParts.fragment) return urlparse.urlunsplit(mergedParts) else: return url