Example #1
0
def qml_style(request, layername, style_name=None):
    """Update/Retrieve QML style of a given QGIS Layer.

    :param layername: The layer name in Geonode.
    :type layername: basestring

    :param style_name: The style name recognized by QGIS Server
    :type style_name: str
    """
    layer = get_object_or_404(Layer, name=layername)

    if request.method == 'GET':

        # Request QML from QGIS server
        if not style_name:
            # If no style name provided, then it is a List request
            styles_obj = None
            try:
                styles_obj = style_list(layer, internal=False)
            except BaseException:
                print 'Failed to fetch styles'

            styles_dict = []
            if styles_obj:
                styles_dict = [model_to_dict(s) for s in styles_obj]

                # If no style returned by GetCapabilities, this is a bug in QGIS
                # Attempt to generate default style name
                if not styles_dict:
                    style_url = style_get_url(layer, 'default')
                    response = requests.get(style_url)
                    if response.status_code == 200:
                        style_url = style_add_url(layer, 'default')
                        with open(layer.qgis_layer.qml_path, 'w') as f:
                            f.write(response.content)
                        response = requests.get(style_url)
                        if response.status_code == 200:
                            styles_obj = style_list(layer, internal=False)
                            styles_dict = [model_to_dict(s) for s in styles_obj]

            response = HttpResponse(
                json.dumps(styles_dict), content_type='application/json')
            return response

        # Return XML file of the style
        style_url = style_get_url(layer, style_name, internal=False)
        response = requests.get(style_url)
        if response.status_code == 200:
            response = HttpResponse(
                response.content, content_type='text/xml')
            response[
                'Content-Disposition'] = 'attachment; filename=%s.qml' % (
                style_name, )
        else:
            response = HttpResponse(
                response.content, status=response.status_code)
        return response
    elif request.method == 'POST':

        # For people who uses API request
        if not request.user.has_perm(
                'change_resourcebase', layer.get_self_resource()):
            return HttpResponse(
                'User does not have permission to change QML style.',
                status=403)

        # Request about adding new QML style

        form = QGISLayerStyleUploadForm(request.POST, request.FILES)

        if not form.is_valid():
            return TemplateResponse(
                request,
                'qgis_server/forms/qml_style.html',
                {
                    'resource': layer,
                    'style_upload_form': form
                },
                status=400).render()

        try:
            uploaded_qml = request.FILES['qml']

            # update qml in uploaded media folder
            # check upload session, is qml file exists?
            layerfile_set = layer.upload_session.layerfile_set
            try:
                qml_layer_file = layerfile_set.get(name='qml')
                # if it is exists, we need to delete it, because it won't be
                # managed by geonode
                qml_layer_file.delete()
            except LayerFile.DoesNotExist:
                pass

            # update qml in QGIS Layer folder
            content = uploaded_qml.read()
            qgis_layer = get_object_or_404(QGISServerLayer, layer=layer)

            with open(qgis_layer.qml_path, mode='w') as f:
                f.write(content)

            # construct URL to post new QML
            style_name = request.POST['name']
            style_title = request.POST['title']
            if not style_name:
                # Assign default name
                name_format = 'style_%Y%m%d%H%M%S'
                current_time = datetime.datetime.utcnow()
                style_name = current_time.strftime(name_format)

            # Add new style
            style_url = style_add_url(layer, style_name)

            response = requests.get(style_url)

            if not (response.status_code == 200 and response.content == 'OK'):
                try:
                    style_list(layer, internal=False)
                except BaseException:
                    print 'Failed to fetch styles'

                return TemplateResponse(
                    request,
                    'qgis_server/forms/qml_style.html',
                    {
                        'resource': layer,
                        'style_upload_form': QGISLayerStyleUploadForm(),
                        'alert': True,
                        'alert_message': response.content,
                        'alert_class': 'alert-danger'
                    },
                    status=response.status_code).render()

            # We succeeded on adding new style

            # Refresh style models
            try:
                style_list(layer, internal=False)
                qgis_style = layer.qgis_layer.styles.get(name=style_name)
                qgis_style.title = style_title
                qgis_style.save()

                alert_message = 'Successfully add style %s' % style_name
            except BaseException:
                alert_message = 'Failed to fetch styles'

            return TemplateResponse(
                request,
                'qgis_server/forms/qml_style.html',
                {
                    'resource': layer,
                    'style_upload_form': form,
                    'alert': True,
                    'alert_class': 'alert-success',
                    'alert_message': alert_message
                },
                status=201).render()

        except Exception as e:
            logger.exception(e)
            return HttpResponseServerError()
    elif request.method == 'DELETE':
        # Request to delete particular QML Style

        if not style_name:
            # Style name should exists
            return HttpResponseBadRequest('Style name not provided.')

        # Handle removing tile-style cache
        try:
            style = layer.qgis_layer.styles.get(name=style_name)
            shutil.rmtree(style.style_tile_cache_path)
        except BaseException:
            pass

        style_url = style_remove_url(layer, style_name)

        response = requests.get(style_url)

        if not (response.status_code == 200 and response.content == 'OK'):
            alert_message = response.content
            if 'NAME is NOT an existing style.' in response.content:
                alert_message = '%s is not an existing style' % style_name
            try:
                style_list(layer, internal=False)
            except BaseException:
                print 'Failed to fetch styles'

            return TemplateResponse(
                request,
                'qgis_server/forms/qml_style.html',
                {
                    'resource': layer,
                    'style_upload_form': QGISLayerStyleUploadForm(),
                    'alert': True,
                    'alert_message': alert_message,
                    'alert_class': 'alert-danger'
                },
                status=response.status_code).render()

        # Successfully removed styles
        # Handle when default style is deleted.
        # Will be handled by style_list method
        try:
            style_list(layer, internal=False)

            alert_message = 'Successfully deleted style %s' % style_name
        except BaseException:
            alert_message = 'Failed to fetch styles'

        return TemplateResponse(
            request,
            'qgis_server/forms/qml_style.html',
            {
                'resource': layer,
                'style_upload_form': QGISLayerStyleUploadForm(),
                'alert': True,
                'alert_message': alert_message,
                'alert_class': 'alert-success'
            },
            status=200).render()

    return HttpResponseBadRequest()
Example #2
0
    def test_style_management_url(self):
        """Test QGIS Server style management url construction."""
        filename = os.path.join(gisdata.GOOD_DATA, 'raster/test_grid.tif')
        uploaded = file_upload(filename)

        # Get default style
        # There will always be a default style when uploading a layer
        style_url = style_get_url(uploaded, 'default', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get('Content-Type'), 'text/xml')

        # it has to contains qgis tags
        style_xml = dlxml.fromstring(response.content)
        self.assertTrue('qgis' in style_xml.tag)

        # Add new style
        # change default style slightly
        self.assertTrue('WhiteToBlack' not in response.content)
        self.assertTrue('BlackToWhite' in response.content)
        new_style_xml = dlxml.fromstring(
            response.content.replace('BlackToWhite', 'WhiteToBlack'))
        new_xml_content = etree.tostring(new_style_xml, pretty_print=True)

        # save it to qml file, accessible by qgis server
        qgis_layer = QGISServerLayer.objects.get(layer=uploaded)
        with open(qgis_layer.qml_path, mode='w') as f:
            f.write(new_xml_content)

        style_url = style_add_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Get style list
        qml_styles = style_list(uploaded, internal=False)
        if qml_styles:
            expected_style_names = ['default', 'new_style']
            actual_style_names = [s.name for s in qml_styles]
            self.assertEqual(set(expected_style_names),
                             set(actual_style_names))

        # Get new style
        style_url = style_get_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get('Content-Type'), 'text/xml')
        self.assertTrue('WhiteToBlack' in response.content)

        # Set default style
        style_url = style_set_default_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Remove style
        style_url = style_remove_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Cleanup
        uploaded.delete()
Example #3
0
    def test_style_management_url(self):
        """Test QGIS Server style management url construction."""
        filename = os.path.join(gisdata.GOOD_DATA, 'raster/test_grid.tif')
        uploaded = file_upload(filename)

        # Get default style
        # There will always be a default style when uploading a layer
        style_url = style_get_url(uploaded, 'default', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get('Content-Type'), 'text/xml')

        # it has to contains qgis tags
        style_xml = etree.fromstring(response.content)
        self.assertTrue('qgis' in style_xml.tag)

        # Add new style
        # change default style slightly
        self.assertTrue('WhiteToBlack' not in response.content)
        self.assertTrue('BlackToWhite' in response.content)
        new_style_xml = etree.fromstring(
            response.content.replace('BlackToWhite', 'WhiteToBlack'))
        new_xml_content = etree.tostring(new_style_xml, pretty_print=True)

        # save it to qml file, accessible by qgis server
        qgis_layer = QGISServerLayer.objects.get(layer=uploaded)
        with open(qgis_layer.qml_path, mode='w') as f:
            f.write(new_xml_content)

        style_url = style_add_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Get style list
        qml_styles = style_list(uploaded, internal=False)
        expected_style_names = ['default', 'new_style']
        actual_style_names = [s.name for s in qml_styles]
        self.assertEqual(set(expected_style_names), set(actual_style_names))

        # Get new style
        style_url = style_get_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.headers.get('Content-Type'), 'text/xml')
        self.assertTrue('WhiteToBlack' in response.content)

        # Set default style
        style_url = style_set_default_url(
            uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Remove style
        style_url = style_remove_url(uploaded, 'new_style', internal=True)

        response = requests.get(style_url)

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.content, 'OK')

        # Cleanup
        uploaded.delete()
Example #4
0
    def from_get_capabilities_style_xml(cls,
                                        qgis_layer,
                                        style_xml,
                                        style_url=None,
                                        synchronize=True):
        """Convert to this model from GetCapabilities Style tag.

        :param qgis_layer: Associated QGIS Server Layer
        :type qgis_layer: QGISServerLayer

        :param style_xml: xml string or object
        :type style_xml: str | lxml.etree.Element |
            xml.etree.ElementTree.Element

        :param style_url: style information stored as xml
        :type style_url: str

        :param synchronize: Flag, if true then synchronize the new value
        :type synchronize: bool

        :return: QGISServerStyle model and boolean flag created
        :rtype: QGISServerStyle, bool
        """

        if isinstance(style_xml, string_types):
            style_xml = dlxml.fromstring(style_xml)

        elif isinstance(style_xml, ElementTree.Element):
            style_xml = dlxml.fromstring(
                ElementTree.tostring(style_xml, encoding='utf-8',
                                     method='xml'))

        namespaces = {
            'wms': 'http://www.opengis.net/wms',
            'xlink': 'http://www.w3.org/1999/xlink'
        }

        filter_dict = {
            'name': style_xml.xpath('wms:Name', namespaces=namespaces)[0].text,
            'layer_styles': qgis_layer
        }

        # if style_body is none, try fetch it from QGIS Server
        if not style_url:
            from geonode.qgis_server.helpers import style_get_url
            style_url = style_get_url(qgis_layer.layer,
                                      filter_dict['name'],
                                      internal=False)

        response = requests.get(style_url)
        style_body = etree.tostring(dlxml.fromstring(
            ensure_string(response.content)),
                                    pretty_print=True)

        default_dict = {
            'title':
            style_xml.xpath('wms:Title', namespaces=namespaces)[0].text,
            'style_legend_url':
            style_xml.xpath('wms:LegendURL/wms:OnlineResource',
                            namespaces=namespaces)
            [0].attrib['{http://www.w3.org/1999/xlink}href'],
            'style_url':
            style_url,
            'body':
            style_body
        }

        # filter_dict['defaults'] = default_dict

        # Can't use get_or_create function for some reason.
        # So use regular query

        try:
            style_obj = QGISServerStyle.objects.get(**filter_dict)
            created = False
        except QGISServerStyle.DoesNotExist:
            style_obj = QGISServerStyle(**default_dict)
            style_obj.name = filter_dict['name']
            style_obj.save()
            created = True

        if created or synchronize:
            # Try to synchronize this model with the given parameters
            style_obj.name = filter_dict['name']

            style_obj.style_url = default_dict['style_url']
            style_obj.body = default_dict['body']
            style_obj.title = default_dict['title']
            style_obj.style_legend_url = default_dict['style_legend_url']
            style_obj.save()

            style_obj.layer_styles.add(qgis_layer)
            style_obj.save()

        return style_obj, created
Example #5
0
    def from_get_capabilities_style_xml(
            cls, qgis_layer, style_xml, style_url=None, synchronize=True):
        """Convert to this model from GetCapabilities Style tag.

        :param qgis_layer: Associated QGIS Server Layer
        :type qgis_layer: QGISServerLayer

        :param style_xml: xml string or object
        :type style_xml: str | lxml.etree.Element |
            xml.etree.ElementTree.Element

        :param style_url: style information stored as xml
        :type style_url: str

        :param synchronize: Flag, if true then synchronize the new value
        :type synchronize: bool

        :return: QGISServerStyle model and boolean flag created
        :rtype: QGISServerStyle, bool
        """

        if isinstance(style_xml, str):
            style_xml = etree.fromstring(style_xml)

        elif isinstance(style_xml, ElementTree.Element):
            style_xml = etree.fromstring(
                ElementTree.tostring(
                    style_xml, encoding='utf-8', method='xml'))

        namespaces = {
            'wms': 'http://www.opengis.net/wms',
            'xlink': 'http://www.w3.org/1999/xlink'
        }

        filter_dict = {
            'name': style_xml.xpath(
                'wms:Name', namespaces=namespaces)[0].text,

            'layer_styles': qgis_layer
        }

        # if style_body is none, try fetch it from QGIS Server
        if not style_url:
            from geonode.qgis_server.helpers import style_get_url
            style_url = style_get_url(
                qgis_layer.layer, filter_dict['name'], internal=False)

        response = requests.get(style_url)
        style_body = etree.tostring(
            etree.fromstring(response.content), pretty_print=True)

        default_dict = {
            'title': style_xml.xpath(
                'wms:Title', namespaces=namespaces)[0].text,

            'style_legend_url': style_xml.xpath(
                'wms:LegendURL/wms:OnlineResource',
                namespaces=namespaces)[0].attrib[
                '{http://www.w3.org/1999/xlink}href'],

            'style_url': style_url,

            'body': style_body
        }

        # filter_dict['defaults'] = default_dict

        # Can't use get_or_create function for some reason.
        # So use regular query

        try:
            style_obj = QGISServerStyle.objects.get(**filter_dict)
            created = False
        except QGISServerStyle.DoesNotExist:
            style_obj = QGISServerStyle(**default_dict)
            style_obj.name = filter_dict['name']
            style_obj.save()
            created = True

        if created or synchronize:
            # Try to synchronize this model with the given parameters
            style_obj.name = filter_dict['name']

            style_obj.style_url = default_dict['style_url']
            style_obj.body = default_dict['body']
            style_obj.title = default_dict['title']
            style_obj.style_legend_url = default_dict['style_legend_url']
            style_obj.save()

            style_obj.layer_styles.add(qgis_layer)
            style_obj.save()

        return style_obj, created