def style_list(layer, internal=True, generating_qgis_capabilities=False): """Query list of styles from QGIS Server. :param layer: Layer to inspect :type layer: Layer :param internal: Flag to switch between public url and internal url. Public url will be served by Django Geonode (proxified). :type internal: bool :param generating_qgis_capabilities: internal Flag for the method to tell that this function were executed to generate QGIS GetCapabilities request for querying Style list. This flag is used for recursion. Default to False as recursion base. :type generating_qgis_capabilities: bool :return: List of QGISServerStyle :rtype: list(QGISServerStyle) """ # We get the list of style from GetCapabilities request # Must call from public URL because we need public LegendURL url = wms_get_capabilities_url(layer, internal=internal) response = requests.get(url) root_xml = etree.fromstring(response.content) styles_xml = root_xml.xpath('wms:Capability/wms:Layer/wms:Layer/wms:Style', namespaces={ 'xlink': 'http://www.w3.org/1999/xlink', 'wms': 'http://www.opengis.net/wms' }) # Fetch styles body try: qgis_layer = QGISServerLayer.objects.get(layer=layer) except QGISServerLayer.DoesNotExist: msg = 'No QGIS Server Layer for existing layer {0}'.format(layer.name) logger.debug(msg) raise styles_obj = [ QGISServerStyle.from_get_capabilities_style_xml(qgis_layer, style_xml)[0] for style_xml in styles_xml ] # Only tried to generate/fix QGIS GetCapabilities to return correct style # list, if: # - the current request return empty styles_obj (no styles, not possible) # - does not currently tried to generate QGIS GetCapabilities to fix this # problem if not styles_obj and not generating_qgis_capabilities: # It's not possible to have empty style. There will always be default # style. # Initiate a dummy requests to trigger build style list on QGIS Server # side # write an empty file if it doesn't exists open(qgis_layer.qml_path, 'a').close() # Basically add a new style then deletes it to force QGIS to refresh # style list in project properties. We don't care the request result. dummy_style_name = '__tmp__dummy__name__' style_url = style_add_url(layer, dummy_style_name) requests.get(style_url) style_url = style_remove_url(layer, dummy_style_name) requests.get(style_url) # End the requests and rely on the next request to build style models # to avoid infinite recursion # Set generating_qgis_capabilities flag to True to avoid next # recursion return style_list(layer, internal=internal, generating_qgis_capabilities=True) # Manage orphaned styles style_names = [s.name for s in styles_obj] for style in qgis_layer.styles.all(): if style.name not in style_names: if style == qgis_layer.default_style: qgis_layer.default_style = None qgis_layer.save() style.delete() # Set default style if not yet set set_default_style = False try: if not qgis_layer.default_style: set_default_style = True except: set_default_style = True if set_default_style and styles_obj: qgis_layer.default_style = styles_obj[0] qgis_layer.save() return styles_obj
def style_list(layer, internal=True, generating_qgis_capabilities=False): """Query list of styles from QGIS Server. :param layer: Layer to inspect :type layer: Layer :param internal: Flag to switch between public url and internal url. Public url will be served by Django Geonode (proxified). :type internal: bool :param generating_qgis_capabilities: internal Flag for the method to tell that this function were executed to generate QGIS GetCapabilities request for querying Style list. This flag is used for recursion. Default to False as recursion base. :type generating_qgis_capabilities: bool :return: List of QGISServerStyle :rtype: list(QGISServerStyle) """ # We get the list of style from GetCapabilities request # Must call from public URL because we need public LegendURL url = wms_get_capabilities_url(layer, internal=internal) response = requests.get(url) root_xml = etree.fromstring(response.content) styles_xml = root_xml.xpath( 'wms:Capability/wms:Layer/wms:Layer/wms:Style', namespaces={ 'xlink': 'http://www.w3.org/1999/xlink', 'wms': 'http://www.opengis.net/wms' }) # Fetch styles body try: qgis_layer = QGISServerLayer.objects.get(layer=layer) except QGISServerLayer.DoesNotExist: msg = 'No QGIS Server Layer for existing layer {0}'.format(layer.name) logger.debug(msg) raise styles_obj = [ QGISServerStyle.from_get_capabilities_style_xml( qgis_layer, style_xml)[0] for style_xml in styles_xml] # Only tried to generate/fix QGIS GetCapabilities to return correct style # list, if: # - the current request return empty styles_obj (no styles, not possible) # - does not currently tried to generate QGIS GetCapabilities to fix this # problem if not styles_obj and not generating_qgis_capabilities: # It's not possible to have empty style. There will always be default # style. # Initiate a dummy requests to trigger build style list on QGIS Server # side # write an empty file if it doesn't exists open(qgis_layer.qml_path, 'a').close() # Basically add a new style then deletes it to force QGIS to refresh # style list in project properties. We don't care the request result. dummy_style_name = '__tmp__dummy__name__' style_url = style_add_url(layer, dummy_style_name) requests.get(style_url) style_url = style_remove_url(layer, dummy_style_name) requests.get(style_url) # End the requests and rely on the next request to build style models # to avoid infinite recursion # Set generating_qgis_capabilities flag to True to avoid next # recursion return style_list( layer, internal=internal, generating_qgis_capabilities=True) # Manage orphaned styles style_names = [s.name for s in styles_obj] for style in qgis_layer.styles.all(): if style.name not in style_names: if style == qgis_layer.default_style: qgis_layer.default_style = None qgis_layer.save() style.delete() # Set default style if not yet set set_default_style = False try: if not qgis_layer.default_style: set_default_style = True except: set_default_style = True if set_default_style and styles_obj: qgis_layer.default_style = styles_obj[0] qgis_layer.save() return styles_obj