예제 #1
0
 def create_layout_from_template(template_filename):
     layout = QgsPrintLayout(QgsProject().instance())
     document = QDomDocument()
     with open(os.path.join('data', 'general',
                            template_filename)) as template_file:
         template_content = template_file.read()
     document.setContent(template_content)
     layout.loadFromTemplate(document, QgsReadWriteContext())
     return layout
예제 #2
0
    def export_view(self):
        """
        Export current view to PDF
        """
        # Load template from file
        s = QSettings()
        f = s.value("cadastre/composerTemplateFile", '', type=str)
        if not os.path.exists(f):
            f = os.path.join(str(Path(__file__).resolve().parent), 'composers',
                             'paysage_a4.qpt')
            s.setValue("cadastre/composerTemplateFile", f)

        QApplication.setOverrideCursor(Qt.WaitCursor)
        template_content = None
        with open(f, 'rt', encoding="utf-8") as ff:
            template_content = ff.read()
        if not template_content:
            return
        d = QDomDocument()
        d.setContent(template_content)

        c = QgsPrintLayout(QgsProject.instance())
        c.loadFromTemplate(d, QgsReadWriteContext())

        # Set scale and extent
        cm = c.referenceMap()
        canvas = self.iface.mapCanvas()
        extent = canvas.extent()
        scale = canvas.scale()
        if extent:
            cm.zoomToExtent(extent)
        if scale:
            cm.setScale(scale)

        # Export
        tempDir = s.value("cadastre/tempDir",
                          '%s' % tempfile.gettempdir(),
                          type=str)
        self.targetDir = tempfile.mkdtemp('', 'cad_export_', tempDir)
        temp = int(time() * 100)
        temppath = os.path.join(tempDir, 'export_cadastre_%s.pdf' % temp)

        exporter = QgsLayoutExporter(c)
        exportersettings = QgsLayoutExporter.PdfExportSettings()
        exportersettings.dpi = 300
        exportersettings.forceVectorOutput = True
        exportersettings.rasterizeWholeImage = False  # rasterizeWholeImage = false
        exporter.exportToPdf(temppath, exportersettings)

        QApplication.restoreOverrideCursor()

        if os.path.exists(temppath):
            CadastreCommon.openFile(temppath)
예제 #3
0
    def layoutLoader(self, template_source, layout_name, title_text):
        """ Generate the layout """
        from qgis.core import (QgsProject, QgsPrintLayout, QgsReadWriteContext)
        from qgis.utils import iface
        from PyQt5.QtXml import QDomDocument

        #template_source = '/home/user/Document/Template.qpt'
        #layout_name = 'NewLayout'
        #title_text = 'New Title'

        # Create objects lm = layout manager, l = print layout
        lm = QgsProject.instance().layoutManager()
        l = QgsPrintLayout(QgsProject.instance())
        l.initializeDefaults()

        # Load template file and load it into the layout (l)
        template_file = open(template_source, 'r+', encoding='utf-8')
        template_content = template_file.read()
        template_file.close()
        document = QDomDocument()
        document.setContent(template_content)
        context = QgsReadWriteContext()
        l.loadFromTemplate(document, context)

        # Give the layout a name (must be unique)
        l.setName(layout_name)

        # Get current canvas extent and apply that to all maps (items) in layout
        # Replace any text "{{title}}" in any layout label with the dialog Title text
        canvas = iface.mapCanvas()
        for item in l.items():
            if item.type() == 65639:  # Map
                item.zoomToExtent(canvas.extent())
            if item.type() == 65641:  # Label
                item.setText(item.text().replace('{{title}}', title_text))

        # Add layout to layout manager
        l.refresh()
        lm.addLayout(l)

        # Open and show the layout in designer
        try:
            iface.openLayoutDesigner(l)
        except:
            oopsBox = QMessageBox()
            oopsBox.setIcon(QMessageBox.Warning)
            oopsBox.setText(
                self.
                tr('Ooops. Something went wrong. Trying to open the generated layout ({}) returned errors.'
                   .format(l.name())))
            oopsBox.setWindowTitle(self.tr('Layout Loader'))
            oopsBox.exec_()
예제 #4
0
    def export_view(self):
        '''
        Export current view to PDF
        '''
        # Load template from file
        s = QSettings()
        f = s.value("cadastre/composerTemplateFile", '', type=str)
        if not os.path.exists(f):
            f = os.path.join(str(Path(__file__).resolve().parent), 'composers', 'paysage_a4.qpt')
            s.setValue("cadastre/composerTemplateFile", f)

        QApplication.setOverrideCursor(Qt.WaitCursor)
        template_content = None
        with open(f, 'rt', encoding="utf-8") as ff:
            template_content = ff.read()
        if not template_content:
            return
        d = QDomDocument()
        d.setContent(template_content)

        c = QgsPrintLayout(QgsProject.instance())
        c.loadFromTemplate(d, QgsReadWriteContext() )

        # Set scale and extent
        cm=c.referenceMap()
        canvas = self.iface.mapCanvas()
        extent = canvas.extent()
        scale = canvas.scale()
        if extent:
            cm.zoomToExtent(extent)
        if scale:
            cm.setScale(scale)

        # Export
        tempDir = s.value("cadastre/tempDir", '%s' % tempfile.gettempdir(), type=str)
        self.targetDir = tempfile.mkdtemp('', 'cad_export_', tempDir)
        temp = int(time()*100)
        temppath = os.path.join(tempDir, 'export_cadastre_%s.pdf' % temp)

        exporter = QgsLayoutExporter(c)
        exportersettings = QgsLayoutExporter.PdfExportSettings()
        exportersettings.dpi = 300
        exportersettings.forceVectorOutput = True
        exportersettings.rasterizeWholeImage = False #rasterizeWholeImage = false
        exporter.exportToPdf(temppath, exportersettings )

        QApplication.restoreOverrideCursor()

        if os.path.exists(temppath):
            cadastre_common.openFile(temppath)
예제 #5
0
    def getPrintLayoutFromQptPath(self, path, newValue):
        '''
        Returns a QgsPrintLayout from a template indicated by path.
        Also sets the composition variable 'variableNames' to newvalue
        '''
        # Load template from file
        layout = QgsPrintLayout(QgsProject.instance())
        # self.updateQptVariables(layout, newValue)
        # layout.initializeDefaults()
        with open(path) as template:
            templateContent = template.read()
        doc = QDomDocument()
        doc.setContent(templateContent)

        # adding to existing items
        #items, ok = layout.loadFromTemplate(doc, QgsReadWriteContext(), False)
        layout.loadFromTemplate(doc, QgsReadWriteContext())
        self.updateQptVariables(layout, str(newValue))
        return layout
예제 #6
0
class ProductExporter(object):
    def __init__(self):
        self.layout = QgsPrintLayout(QgsProject.instance())

    def populateTemplate(self, templateXMLContent):
        templateDomDoc = QDomDocument()
        templateDomDoc.setContent(templateXMLContent)
        #TODO: Adicionar um parser para o XML para substituir
        # informações pertinentes, como por exemplo, id da
        # camada de atlas.
        self.layout.loadFromTemplate(templateDomDoc, QgsReadWriteContext())

    def export(self, parameterDict):
        pass

    def exportPdf(self, outputPath, feedback=None):
        exporter = QgsLayoutExporter(self.layout)
        result, error = exporter.exportToPdfs(
            self.layout.atlas(),
            outputPath,
            settings=QgsLayoutExporter.PdfExportSettings(),
            feedback=feedback)
        return result, error

    def exportTiff(self):
        pass

    def generateProductionStepsHTML(self, parameterDict):
        """
        Para gerar a tabela de etapas de produção.
        Nos parâmetros deve vir a ordem de supressão caso seja necessário.
        """
        pass

    def exportPopulatedQPT(self, parameterDict):
        """
        easter egg
        """
        pass
    def get_print_layout(self):
        template_path = self.getTemplateFilePath()
        if not os.path.isfile(template_path):
            msg = 'The requested template {} is not currently available.'.format(
                template_path)
            QMessageBox.critical(self.iface.mainWindow(), 'Template Not Found',
                                 msg)
            return

        # Create a new print layout with name equal to the project title
        project = QgsProject.instance()
        layout_manager = project.layoutManager()
        user_layout_title = self.ui.titleLineEdit.text()
        layout_title = user_layout_title if user_layout_title else "unnamed"
        existing_print_layout = layout_manager.layoutByName(
            layout_title) if layout_title else None
        if existing_print_layout:
            layout_manager.removeLayout(existing_print_layout)
        print_layout = QgsPrintLayout(project)

        # Load the template file
        try:
            tree = ET.parse(template_path)
            doc = QDomDocument()
            doc.setContent(ET.tostring(tree.getroot()))
        except IOError:
            # problem reading xml template
            msg = 'The requested template {} could not be read.'.format(
                template_path)
            QMessageBox.critical(self.iface.mainWindow(),
                                 'Failed to Read Template', msg)
            return
        except:
            # Unexpected problem
            msg = 'An unexpected error occurred while reading {}:\n\n{}'.format(
                template_path, traceback.format_exc())
            QMessageBox.critical(self.iface.mainWindow(),
                                 'Failed to Read Template', msg)
            return

        if not print_layout.loadFromTemplate(doc, QgsReadWriteContext(), True):
            msg = 'loadFromTemplate returned False.'
            QMessageBox.critical(self.iface.mainWindow(),
                                 'Failed to Read Template', msg)
            return

        print_layout.setName(layout_title)
        return print_layout
예제 #8
0
def open_template(layout: QgsPrintLayout, fp: str):
  '''Load items from template into layout'''
  logging.info(f'opening template: {fp}')
  doc = QDomDocument()
  context = QgsReadWriteContext()

  # read template
  with open(fp, 'rt', encoding='utf-8') as f:
    content = f.read()

  # set content and load items from template
  doc.setContent(content)
  _, ok = layout.loadFromTemplate(doc, context)
  if not ok:
    raise baseException(f'failed to load QGS template: {fp}',
                        baseException.ERR_CODE_LEVEL)
예제 #9
0
    def open_in_composer(self):
        """Open in layout designer a given MapReport instance.

        .. versionadded: 4.3.0
        """
        impact_layer = self.impact_function.analysis_impacted
        report_path = dirname(impact_layer.source())

        impact_report = self.impact_function.impact_report
        custom_map_report_metadata = impact_report.metadata
        custom_map_report_product = (
            custom_map_report_metadata.component_by_tags(
                [final_product_tag, pdf_product_tag]))

        for template_path in self.retrieve_paths(
                custom_map_report_product,
                report_path=report_path,
                suffix='.qpt'):

            layout = QgsPrintLayout(QgsProject.instance())

            with open(template_path) as template_file:
                template_content = template_file.read()

            document = QtXml.QDomDocument()
            document.setContent(template_content)

            # load layout object
            rwcontext = QgsReadWriteContext()
            load_status = layout.loadFromTemplate(document, rwcontext)

            if not load_status:
                # noinspection PyCallByClass,PyTypeChecker
                QtWidgets.QMessageBox.warning(
                    self,
                    tr('InaSAFE'),
                    tr('Error loading template: %s') % template_path)

                return

            QgsProject.instance().layoutManager().addLayout(layout)
            self.iface.openLayoutDesigner(layout)
예제 #10
0
    def open_in_composer(self):
        """Open in layout designer a given MapReport instance.

        .. versionadded: 4.3.0
        """
        impact_layer = self.impact_function.analysis_impacted
        report_path = dirname(impact_layer.source())

        impact_report = self.impact_function.impact_report
        custom_map_report_metadata = impact_report.metadata
        custom_map_report_product = (
            custom_map_report_metadata.component_by_tags(
                [final_product_tag, pdf_product_tag]))

        for template_path in self.retrieve_paths(
                custom_map_report_product,
                report_path=report_path,
                suffix='.qpt'):

            layout = QgsPrintLayout(QgsProject.instance())

            with open(template_path) as template_file:
                template_content = template_file.read()

            document = QtXml.QDomDocument()
            document.setContent(template_content)

            # load layout object
            rwcontext = QgsReadWriteContext()
            load_status = layout.loadFromTemplate(document, rwcontext)

            if not load_status:
                # noinspection PyCallByClass,PyTypeChecker
                QtWidgets.QMessageBox.warning(
                    self,
                    tr('InaSAFE'),
                    tr('Error loading template: %s') % template_path)

                return

            QgsProject.instance().layoutManager().addLayout(layout)
            self.iface.openLayoutDesigner(layout)
    def get_print_layout(self):
        template_path = self.getTemplateFilePath()
        if not os.path.isfile(template_path):
            msg = 'The requested template {} is not currently available.'.format(template_path)
            QMessageBox.critical(self.iface.mainWindow(), 'Template Not Found', msg)
            return

        # Create a new print layout with name equal to the project title
        project = QgsProject.instance()
        layout_manager = project.layoutManager()
        existing_print_layout = layout_manager.layoutByName(self.ui.titleLineEdit.text())
        if existing_print_layout:
            layout_manager.removeLayout(existing_print_layout)
        print_layout = QgsPrintLayout(project)
        print_layout.setName(self.ui.titleLineEdit.text())
        layout_manager.addLayout(print_layout)

        # Load the template file
        try:
            tree = ET.parse(template_path)
            doc = QDomDocument()
            doc.setContent(ET.tostring(tree.getroot()))
        except IOError:
            # problem reading xml template
            msg = 'The requested template {} could not be read.'.format(template_path)
            QMessageBox.critical(self.iface.mainWindow(), 'Failed to Read Template', msg)
            return
        except:
            # Unexpected problem
            msg = 'An unexpected error occurred while reading {}:\n\n{}'.format(template_path, traceback.format_exc())
            QMessageBox.critical(self.iface.mainWindow(), 'Failed to Read Template', msg)
            return

        if not print_layout.loadFromTemplate(doc, QgsReadWriteContext(), True):
            msg = 'loadFromTemplate returned False.'
            QMessageBox.critical(self.iface.mainWindow(), 'Failed to Read Template', msg)
            return

        return print_layout
예제 #12
0
    def createLayout(self):
        print(u'createLayout()')

        templateFile = self.createLayoutDialog.comboBoxTemplate.currentText()
        templateQpt = os.path.join(self.dataPath, templateFile)

        layoutName = self.createLayoutDialog.lineEditName.text()

        project = QgsProject.instance()
        layoutManager = project.layoutManager()

        oldLayout = layoutManager.layoutByName(layoutName)
        if oldLayout is not None:
            print(u'removing: {}'.format(oldLayout))
            layoutManager.removeLayout(oldLayout)

        # create new layout
        layout = QgsPrintLayout(project)

        # load layout template
        fl = QFile(templateQpt)
        doc = QDomDocument()
        doc.setContent(fl)
        layout.loadFromTemplate(doc, QgsReadWriteContext())

        # set name
        layout.setName(layoutName)

        # set map properties
        map = self.getItemById(layout, u'RC_map')
        if map is not None:
            #print(map.crs().description())
            map.zoomToExtent(self.iface.mapCanvas().extent())

        # set default label values
        for configurationItem in simsLayoutConfiguration:
            label = self.getItemById(layout, configurationItem['code'])
            if label is not None and isinstance(label, QgsLayoutItemLabel):
                if configurationItem['default'] is not None:
                    label.setText(configurationItem['default'])

        # set overview map
        map = self.getItemById(layout, u'RC_overview')
        if map is not None:
            worldLayer = self.addWorldLayer()
            map.setFollowVisibilityPreset(False)
            map.setKeepLayerSet(True)
            map.setLayers([worldLayer])

            overviewCrs = QgsCoordinateReferenceSystem("EPSG:54030")
            map.setCrs(overviewCrs)

            extent = worldLayer.extent()
            layerCrs = worldLayer.sourceCrs()
            if not layerCrs == overviewCrs:
                transform = QgsCoordinateTransform(layerCrs, overviewCrs,
                                                   QgsProject.instance())
                extent = transform.transformBoundingBox(extent)
            map.zoomToExtent(extent)

            root = QgsProject.instance().layerTreeRoot()
            rl = root.findLayer(worldLayer.id())
            rl.setItemVisibilityChecked(False)

        # set disclamer
        languageChoice = self.createLayoutDialog.comboBoxLanguage.currentText()

        label = self.getItemById(layout, u'RC_disclaimer')
        if label is not None:
            label.setText(simsDisclamers[languageChoice])

        label = self.getItemById(layout, u'RC_logotext')
        if label is not None:
            label.setText(simsLogoTexts[languageChoice])

        # set title
        label = self.getItemById(layout, u'RC_title')
        if label is not None:
            label.setText(self.createLayoutDialog.lineEditName.text())

        # set Copyright
        # possibly dynamically: [%'© SIMS '  || year(now())%]
        '''
        label = self.getItemById(layout, u'COPYRIGHT')
        if label is not None:
            print(label)
            label.setText(u'© SIMS {0}'.format(datetime.now().year))

        # set filename
        label = self.getItemById(layout, u'FILENAME')
        if label is not None:
            print(label)
            filename = QgsProject.instance().fileName()
            if filename == u'':
                filename = u'filename unknown, project not saved'
            label.setText(filename)
        '''

        # set NS logo
        picture = self.getItemById(layout, u'RC_logo1')
        if picture is not None:
            logoChoice = self.createLayoutDialog.comboBoxNsLogo.currentText()
            logoSvg = os.path.join(self.dataPath, u'logos', logoChoice)
            picture.setPicturePath(logoSvg)

        # set IFRC logo
        picture = self.getItemById(layout, u'RC_logo2')
        if picture is not None:
            logo = simsIfrcLogos[languageChoice]
            logoSvg = os.path.join(self.dataPath, u'img', logo)
            picture.setPicturePath(logoSvg)

        # set date
        label = self.getItemById(layout, u'RC_date')
        if label is not None:
            now = datetime.now()
            month = simsMonths[languageChoice][now.month]
            label.setText(now.strftime('%d {} %Y').format(month))

        # set North Arrow
        picture = self.getItemById(layout, u'RC_northarrow')
        if picture is not None:
            logoSvg = os.path.join(QgsApplication.pkgDataPath(), u'svg',
                                   u'arrows', u'NorthArrow_02.svg')
            picture.setPicturePath(logoSvg)

        # clear default label values

        # add to project and open designer window
        layoutManager.addLayout(layout)
        designer = self.iface.openLayoutDesigner(layout)
예제 #13
0
class run(QObject):
    def __init__(self, id, gtotool, config, debug):
        super(run, self).__init__()
        try:
            self.debug = debug
            self.gtotool=gtotool
            self.gtomain = gtotool.gtomain
            self.info =gtotool.info
            self.metadata = self.gtomain.metadata
            templatedir = self.metadata.dirPrintLayouts
            self.iface = self.gtomain.iface
            #tool data
            template = config.get('template',None)
            activetool= config.get('active_tool',None)
            scale = config.get('scale',0)
            #init
            templatefile = None
            layoutname =config.get('layoutname', None)
            if template and layoutname is None:
                layoutname= os.path.splitext(template)[0]#default layoutname for template
            if self.debug:self.info.log("layoutname:",layoutname)
            prj = QgsProject.instance()
            projectLayoutManager = prj.layoutManager()#QgsLayoutManager
            self.layout = None
            if template is not None:
                templatefile = os.path.join(templatedir , template)
                if debug: self.info.log("template:",templatefile)
                if os.path.isfile(templatefile):
                    f= open(templatefile, 'r')
                    templateContent = f.read()
                    f.close()
                    doc=QDomDocument()
                    doc.setContent(templateContent)
                    pr = QgsPathResolver(templatefile)
                    rwc = QgsReadWriteContext()
                    rwc.setPathResolver(pr)
                    self.layout = QgsPrintLayout(prj)
                    self.layout.loadFromTemplate(doc,rwc)
                    self.layout.setName(layoutname)
                    projectLayoutManager.addLayout(self.layout)
            self.layout = projectLayoutManager.layoutByName(layoutname)
            if self.debug: self.info.log(type(self.layout))
            if self.debug and self.layout:self.info.log("found layout:",self.layout.name())
            if self.layout:
                result = self.iface.openLayoutDesigner(self.layout)#QgsLayoutDesignerInterface

                self.layoutview = result.view()#QgsLayoutView
                currenttool = self.layoutview.tool()#QgsLayoutViewTool
                if self.debug: self.info.log(currenttool.toolName())

                if activetool:
                    tool = QgsLayoutViewToolMoveItemContent(self.layoutview)
                    self.layoutview.setTool(tool)

                #itemMap = QgsLayoutItemMap(self.layout)
                referencemap = self.layout.referenceMap()
                referencemap.zoomToExtent(self.iface.mapCanvas().extent())
                if scale < 0:
                    referencemap.setScale(self.iface.mapCanvas().scale())
                elif scale > 0:
                    referencemap.setScale(scale)
                referencemap.refresh()

                if activetool:
                    menu = result.editMenu()
                    for a in menu.actions():
                        if a.objectName() == activetool:
                            if self.debug: self.info.log("set active tool:",activetool)
                            a.trigger()
                            break
                self.result = True
            else:
                self.result="Layout <%s> not found!" % layoutname
        except Exception as e:
            self.info.err(e)
예제 #14
0
    def print_atlas(project_path, composer_name, predefined_scales, feature_filter, page_name_expression=None):
        if not feature_filter:
            QgsMessageLog.logMessage("atlasprint: NO feature_filter provided !", 'atlasprint', Qgis.Critical)
            return None

        # Get composer from project
        # in QGIS 2, we can't get composers without iface
        # so we reading project xml and extract composer
        # TODO Since QGIS 3.0, we should be able to use project layoutManager()
        # noinspection PyPep8Naming
        from xml.etree import ElementTree as ET
        composer_xml = None
        with open(project_path, 'r') as f:
            tree = ET.parse(f)
            for elem in tree.findall('.//Composer[@title="%s"]' % composer_name):
                composer_xml = ET.tostring(
                    elem,
                    encoding='utf8',
                    method='xml'
                )
            if not composer_xml:
                for elem in tree.findall('.//Layout[@name="%s"]' % composer_name):
                    composer_xml = ET.tostring(
                        elem,
                        encoding='utf8',
                        method='xml'
                    )

        if not composer_xml:
            QgsMessageLog.logMessage("atlasprint: Composer XML not parsed !", 'atlasprint', Qgis.Critical)
            return None

        document = QDomDocument()
        document.setContent(composer_xml)

        # Get canvas, map setting & instantiate composition
        canvas = QgsMapCanvas()
        project = QgsProject()
        project.read(project_path)
        bridge = QgsLayerTreeMapCanvasBridge(
            project.layerTreeRoot(),
            canvas
        )
        bridge.setCanvasLayers()

        layout = QgsPrintLayout(project)

        # Load content from XML
        layout.loadFromTemplate(
            document,
            QgsReadWriteContext(),
        )

        atlas = layout.atlas()
        atlas.setEnabled(True)

        atlas_map = layout.referenceMap()
        atlas_map.setAtlasDriven(True)
        atlas_map.setAtlasScalingMode(QgsLayoutItemMap.Predefined)

        layout.reportContext().setPredefinedScales(predefined_scales)

        if page_name_expression:
            atlas.setPageNameExpression(page_name_expression)

        # Filter feature here to avoid QGIS looping through every feature when doing : composition.setAtlasMode(QgsComposition.ExportAtlas)
        coverage_layer = atlas.coverageLayer()

        # Filter by FID as QGIS cannot compile expressions with $id or other $ vars
        # which leads to bad performance for big dataset
        use_fid = None
        if '$id' in feature_filter:
            import re
            ids = list(map(int, re.findall(r'\d+', feature_filter)))
            if len(ids) > 0:
                use_fid = ids[0]
        if use_fid:
            qReq = QgsFeatureRequest().setFilterFid(use_fid)
        else:
            qReq = QgsFeatureRequest().setFilterExpression(feature_filter)

        # Change feature_filter in order to improve performance
        pks = coverage_layer.dataProvider().pkAttributeIndexes()
        if use_fid and len(pks) == 1:
            pk = coverage_layer.dataProvider().fields()[pks[0]].name()
            feature_filter = '"%s" IN (%s)' % (pk, use_fid)
            QgsMessageLog.logMessage("atlasprint: feature_filter changed into: %s" % feature_filter, 'atlasprint', Qgis.Info)
            qReq = QgsFeatureRequest().setFilterExpression(feature_filter)
        atlas.setFilterFeatures(True)
        atlas.setFilterExpression(feature_filter)
        uid = uuid4()
        i = 0  # We use a single page for now.

        atlas.beginRender()
        atlas.seekTo(i)

        # setup settings
        settings = QgsLayoutExporter.PdfExportSettings()
        export_path = os.path.join(
                tempfile.gettempdir(),
                '%s_%s.pdf' % (atlas.nameForPage(i), uid)
        )
        exporter = QgsLayoutExporter(layout)
        result = exporter.exportToPdf(export_path, settings)

        atlas.endRender()
        if result != QgsLayoutExporter.Success:
            QgsMessageLog.logMessage("atlasprint: export not generated %s" % export_path, 'atlasprint', Qgis.Critical)
            return None

        if not os.path.isfile(export_path):
            QgsMessageLog.logMessage("atlasprint: export not generated %s" % export_path, 'atlasprint', Qgis.Critical)
            return None

        QgsMessageLog.logMessage("atlasprint: path generated %s" % export_path, 'atlasprint', Qgis.Success)
        return export_path
예제 #15
0
class ScangisAtlas:
    def __init__(self, iface):
        self.iface = iface
        self.virtualLayerName = u'deelnemers_atlas'
        self.layoutName = u'Scangis Atlas'

        #print(__file__)
        self.pluginDir = os.path.dirname(__file__)
        #print(self.pluginDir)
        self.templateFile = os.path.join(self.pluginDir, u'data', u'a4.qpt')
        self.scangisStyle = os.path.join(self.pluginDir, u'data',
                                         u'pakketten_atlas_alz.qml')
        #print(self.templateFile)

    def initGui(self):
        self.action = QAction(u'Scangis Atlas', self.iface.mainWindow())
        self.action.triggered.connect(self.run)
        self.iface.addToolBarIcon(self.action)

    def unload(self):
        self.iface.removeToolBarIcon(self.action)
        del self.action

    def run(self):
        project = QgsProject.instance()

        # set qml
        scangisLayer = self.iface.activeLayer()
        #print(scangisLayer.name())
        scangisLayer.loadNamedStyle(self.scangisStyle)

        # delete existing deelnemers virtual layer
        oldLayers = project.mapLayersByName(self.virtualLayerName)
        for oldLayer in oldLayers:
            project.removeMapLayer(oldLayer.id())

        # create new virtual layer
        query = u'?query=SELECT min(ID) AS ID, DEELN_NAAM, st_union(geometry) AS geometry FROM "{}" GROUP BY DEELN_NAAM'.format(
            scangisLayer.name())
        virtualLayer = QgsVectorLayer(query, self.virtualLayerName, "virtual")
        project.addMapLayer(virtualLayer)

        ltr = QgsProject.instance().layerTreeRoot()
        rootLayer = ltr.findLayer(virtualLayer.id())
        rootLayer.setItemVisibilityChecked(False)

        self.iface.mapCanvas().refreshAllLayers()

        # delete existing atlas layout
        layoutManager = project.layoutManager()
        #print(layoutManager)
        #print(layoutManager.layouts())
        oldLayout = layoutManager.layoutByName(self.layoutName)
        if oldLayout is not None:
            layoutManager.removeLayout(oldLayout)

        # create new layout
        self.atlasLayout = QgsPrintLayout(project)

        # load layout template
        fl = QFile(self.templateFile)
        doc = QDomDocument()
        doc.setContent(fl)
        rw_context = QgsReadWriteContext()
        rw_context.setPathResolver(project.pathResolver())
        self.atlasLayout.loadFromTemplate(doc, rw_context)

        # set name
        self.atlasLayout.setName(self.layoutName)

        # add to project
        layoutManager.addLayout(self.atlasLayout)

        # turn on atlas
        atl = self.atlasLayout.atlas()
        atl.setEnabled(True)
        atl.setCoverageLayer(virtualLayer)
        atl.setHideCoverage(True)
        itemId = u'map1'
        item = self.getLayoutItem(itemId, QgsLayoutItemMap)
        item.setAtlasDriven(True)

        # set title
        itemId = u'label_title'
        item = self.getLayoutItem(itemId, QgsLayoutItemLabel)
        #item.setText(u'[% attribute(@atlas_feature, \'DEELN_NAAM\') %]')
        item.setText(u'[% "DEELN_NAAM" %]')

        # set source info
        itemId = u'label_source'
        item = self.getLayoutItem(itemId, QgsLayoutItemLabel)
        exp = u"[% format_date(now(), 'dd-MM-yyyy (hh:mm:ss)') || '\\n' || 'pagina ' || @atlas_featurenumber || '/' ||  @atlas_totalfeatures %]"
        item.setText(exp)
        item.setHAlign(2)  # right
        item.setVAlign(64)  # bottom

        # set legend filter
        itemId = u'legend1'
        item = self.getLayoutItem(itemId, QgsLayoutItemLegend)
        #print(item)
        item.setLegendFilterByMapEnabled(True)

        #QMessageBox.information(None, u'Scangis Atlas', u'Atlas finished! :)')
        designer = self.iface.openLayoutDesigner(self.atlasLayout)

        # new in qgis 3.3
        if qgis.utils.Qgis.QGIS_VERSION_INT >= 30300:
            designer.setAtlasPreviewEnabled(True)

    def getLayoutItem(self, itemId, itemType):
        item = self.atlasLayout.itemById(itemId)
        if item is None:
            #print(u'Layout does not contain item: \'{0}\''.format(itemId))
            return None
        if not type(item) == u'qgis._core.{0}'.format(itemType.__name__):
            item = sip.cast(item, itemType)
        return item
예제 #16
0
 def _load_print_layout(self, layer_name, prod_id, dt=None):
     
     window_title = "Print"    # will be checked with a found composer title
     
     """
     Avoid creating a new PrintComposer again and again here.
     Otherwise more and more composers will be added to the list - even with the same name.
     """
     
     project = QgsProject.instance()
     # From it, you can get the current layoutManager instance and deduce the layouts
     layout_manager = project.layoutManager()
     
     layout = layout_manager.layoutByName(window_title)
     
     if not layout:
         self.out("no composer found; creating one...")
         
         # Load the template into the composer
         # QGIS 2:
         #active_composer = self.iface.createNewComposer(window_title)    #createNewComposer()
         #active_composer = QgsComposition(QgsProject.instance())
         
         #layout = QgsLayout(project)
         layout = QgsPrintLayout(project)
         layout.initializeDefaults()    # initializes default settings for blank print layout canvas
         
         
         q_xmldoc = self._create_qdocument_from_print_template_content()
         
         # load layout from template and add to Layout Manager
         #layout.composition().loadFromTemplate(q_xmldoc)    # QGIS 2
         layout.loadFromTemplate(q_xmldoc, QgsReadWriteContext())
         layout.setName(window_title)
         
         layout_manager.addLayout(layout)
         
         
         # Update Logo:
         
         #logo_item = layout.getComposerItemById('logo')    # QGIS 2
         logo_item = layout.itemById('logo')
         #self.out(">>> logo_item: '{}'".format(logo_item))    # gibt nur die Objekt-ID aus
         logo_image = self._model.logo_path
         self.out("Logo: {}".format(logo_image))
         if logo_image.exists():
             logo_item.setPicturePath(str(logo_image))
         else:
             self.out("  ERROR: logo '{}' not found!".format(logo_image), False)
     # if
     
     
     """
     Hier versuche ich ein für die Überschrift mit einer ID ('headline') versehenes
     QgsLabel aus dem Template ausfindig zu machen. Ich mache das hier sehr kompliziert,
     es gibt bestimmt einen einfacheren Weg.
     Folgendes hat NICHT funktioniert:
     map_item = active_composer.getComposerItemById('headline')
     print(active_composer.items())
     liefert: [<PyQt4.QtGui.QGraphicsRectItem object at 0x124c60e8>, <qgis._core.QgsComposerLabel object at 0x124c65a8>, ... ]
     """
     
     ''' other possibility:
     for item in list(layout.items()):
         #if type(item) != QgsComposerLabel:
     '''
     
     # QgsComposerLabel:
     composer_label = layout.itemById('headline')    # a QgsComposerLabel was provided with the ID 'headline' in the template
     # -> None if not found
     
     if composer_label:
         title    = self._model.title(prod_id, dt)
         subtitle = ""
         if prod_id != 'RW':
             subtitle = "\n{}-Produkt (Basis: RW)".format(prod_id)
         
         composer_label.setText(title + subtitle)
     else:
         # A note that the template needs to be revised:
         self.out("no element with id 'headline' found!", False)
     
     
     
     legend = layout.itemById('legend')
     
     if not legend:
         self.out("legend couldn't created!", False)
         return
     
     
     #
     # Layer für die Legende ausfindig machen
     #
     
     # You would just need to make sure your layer has a name you can distinguish from others. Instead of:
     # Vorherige Version:
     #active_raster_layer = self.iface.activeLayer()
     
     # do:
     l_layer = project.mapLayersByName(layer_name)
     
     if not l_layer:
         self.out("legend: no layer found with name '{}'!".format(layer_name), False)
         return
     
     active_raster_layer = l_layer[0]
     
     #print("Legend active_raster_layer id:",   active_raster_layer.id())     # ok
     #print("Legend active_raster_layer name:", active_raster_layer.name())   # ok
     #legend.model().setLayerSet([layer.id() for layer in layers])
     #legend.model().setLayerSet([active_raster_layer.id()])    # bringt nichts
     # DAS ist es! Dies fügt zumindest erstmal das interessierende Rasterlayer hinzu:
     #legend.modelV2().rootGroup().addLayer(active_raster_layer)
     #legend.updateLegend()
     
     #for layout in layout_manager.printLayouts():    # iterate layouts
     
     ''' would be ok, if we want to create a new legend -> then legend appears at the upper left corner
     legend = QgsLayoutItemLegend(layout)
     #legend.setTitle('Legend')
     legend.setAutoUpdateModel(False)
     group = legend.model().rootGroup()
     group.clear()
     group.addLayer(active_raster_layer)
     layout.addItem(legend)
     legend.adjustBoxSize()
     #legend.refresh()    # avoids adding all other layers 
     '''
     
     # uses existing legend object (see above), so we preserve it's layout position:
     legend.setAutoUpdateModel(False)
     group = legend.model().rootGroup()
     group.clear()
     group.addLayer(active_raster_layer)
     legend.adjustBoxSize()
     
     """ By default the newly created composer items have zero position (top left corner of the page) and zero size.
     The position and size are always measured in millimeters.
     # set label 1cm from the top and 2cm from the left of the page
     composerLabel.setItemPosition(20, 10)
     # set both label’s position and size (width 10cm, height 3cm)
     composerLabel.setItemPosition(20, 10, 100, 30)
     A frame is drawn around each item by default. How to remove the frame:
     composerLabel.setFrame(False)
     """
     #print(active_composer.rect().width(), active_composer.rect().height())                   # 1054 911
     #print(self.iface.mapCanvas().size().width(), self.iface.mapCanvas().size().height())     # 1517 535
     # "Leinwandgröße": habe keine vernünftigen Werte oben ermittelt (vielleicht Pixel; ich brauche mm).
     # selbst, mittels Mauszeiger ermittelt:
     width  = 210
     height = 297
     # Rand neben der Legende (mm):
     dw = 10
     dh = 14
     
     """
예제 #17
0
파일: default.py 프로젝트: inasafe/inasafe
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context
    qgis_composition_context = impact_report.qgis_composition_context

    # load composition object
    layout = QgsPrintLayout(QgsProject.instance())

    # load template
    main_template_folder = impact_report.metadata.template_folder

    # we do this condition in case custom template was found
    if component.template.startswith('../qgis-composer-templates/'):
        template_path = os.path.join(main_template_folder, component.template)
    else:
        template_path = component.template

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()

    # Replace
    for k, v in context.substitution_map.items():
        template_content = template_content.replace('[{}]'.format(k), v)

    document.setContent(template_content)

    rwcontext = QgsReadWriteContext()
    load_status = layout.loadFromTemplate(
        document, rwcontext)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = layout_item(layout, item_id, QgsLayoutItemPicture)
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = layout_item(layout, item_id, QgsLayoutItemHtml)
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsLayoutItemHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsLayoutItemHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    original_crs = impact_report.impact_function.crs
    destination_crs = qgis_composition_context.map_settings.destinationCrs()
    coord_transform = QgsCoordinateTransform(original_crs,
                                             destination_crs,
                                             QgsProject.instance())

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = [
            layer for layer in map_el.get('layers') if isinstance(
                layer, QgsMapLayer)
        ]
        map_extent_option = map_el.get('extent')
        composer_map = layout_item(layout, item_id, QgsLayoutItemMap)

        for index, layer in enumerate(layers):
            # we need to check whether the layer is registered or not
            registered_layer = (
                QgsProject.instance().mapLayer(layer.id()))
            if registered_layer:
                if not registered_layer == layer:
                    layers[index] = registered_layer
            else:
                QgsProject.instance().addMapLayer(layer)

        """:type: qgis.core.QgsLayoutItemMap"""
        if composer_map:

            # Search for specified map extent in the template.
            min_x = composer_map.extent().xMinimum() if (
                impact_report.use_template_extent) else None
            min_y = composer_map.extent().yMinimum() if (
                impact_report.use_template_extent) else None
            max_x = composer_map.extent().xMaximum() if (
                impact_report.use_template_extent) else None
            max_y = composer_map.extent().yMaximum() if (
                impact_report.use_template_extent) else None

            composer_map.setKeepLayerSet(True)
            layer_set = [l for l in layers if isinstance(l, QgsMapLayer)]
            composer_map.setLayers(layer_set)
            map_overview_extent = None
            if map_extent_option and isinstance(
                    map_extent_option, QgsRectangle):
                # use provided map extent
                extent = coord_transform.transform(map_extent_option)
                for l in [layer for layer in layers if
                          isinstance(layer, QgsMapLayer)]:
                    layer_extent = coord_transform.transform(l.extent())
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for l in [layer for layer in layers if
                          isinstance(layer, QgsMapLayer)]:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(l.extent())
                    extent.combineExtentWith(layer_extent)
                    if l.name() == map_overview['id']:
                        map_overview_extent = layer_extent

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = min_x or (center.x() - half_length - margin)
            max_x = max_x or (center.x() + half_length + margin)
            min_y = min_y or (center.y() - half_length - margin)
            max_y = max_y or (center.y() + half_length + margin)

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            if component.key == 'population-infographic' and (
                    map_overview_extent):
                square_extent = map_overview_extent

            composer_map.zoomToExtent(square_extent)
            composer_map.invalidateCache()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = [
            layer for layer in leg_el.get('layers') if isinstance(
                layer, QgsMapLayer)
        ]
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = layout_item(layout, item_id, QgsLayoutItemLegend)
        """:type: qgis.core.QgsLayoutItemLegend"""
        if legend:
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 7:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 7 + 1)

            # set legend title
            if title is not None and not impact_report.legend_layers:
                legend.setTitle(title)

            # set legend
            root_group = legend.model().rootGroup()
            for layer in layers:
                # we need to check whether the layer is registered or not
                registered_layer = (
                    QgsProject.instance().mapLayer(layer.id()))
                if registered_layer:
                    if not registered_layer == layer:
                        layer = registered_layer
                else:
                    QgsProject.instance().addMapLayer(layer)
                # used for customizations
                tree_layer = root_group.addLayer(layer)
                if impact_report.legend_layers or (
                        not impact_report.multi_exposure_impact_function):
                    QgsLegendRenderer.setNodeLegendStyle(
                        tree_layer, QgsLegendStyle.Hidden)
            legend.adjustBoxSize()
            legend.updateFilterByMap(False)

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in list(output_format.items()):
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output[key] = result_path
    elif (output_format in
            QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(
                impact_report,
                component_output_path,
                layout,
                output_format,
                component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(
                component_output_path, layout)
            component_output = result_path

    component.output = component_output

    return component.output
예제 #18
0
    def run(self, *args, **kwargs):
        """
        :param templatePath: The file path to the user-defined template.
        :param entityFieldName: The name of the column for the specified entity which
        must exist in the data source view or table.
        :param entityFieldValue: The value for filtering the records in the data source
        view or table.
        :param outputMode: Whether the output composition should be an image or PDF.
        :param filePath: The output file where the composition will be written to. Applies
        to single mode output generation.
        :param dataFields: List containing the field names whose values will be used to name the files.
        This is used in multiple mode configuration.
        :param fileExtension: The output file format. Used in multiple mode configuration.
        :param data_source: Name of the data source table or view whose
        row values will be used to name output files if the options has been
        specified by the user.
        """
        templatePath = args[0]
        entityFieldName = args[1]
        entityFieldValue = args[2]
        outputMode = args[3]
        filePath = kwargs.get("filePath", None)
        dataFields = kwargs.get("dataFields", [])
        fileExtension = kwargs.get("fileExtension", "")
        data_source = kwargs.get("data_source", "")

        templateFile = QFile(templatePath)

        if not templateFile.open(QIODevice.ReadOnly):
            return False, QApplication.translate("DocumentGenerator",
                                            "Cannot read template file.")

        templateDoc = QDomDocument()

        if templateDoc.setContent(templateFile):
            composerDS = ComposerDataSource.create(templateDoc)
            spatialFieldsConfig = SpatialFieldsConfiguration.create(templateDoc)
            composerDS.setSpatialFieldsConfig(spatialFieldsConfig)

            #Check if data source exists and return if it doesn't
            if not self.data_source_exists(composerDS):
                msg = QApplication.translate("DocumentGenerator",
                                             "'{0}' data source does not exist in the database."
                                             "\nPlease contact your database "
                                             "administrator.".format(composerDS.name()))
                return False, msg

            #Set file name value formatter
            self._file_name_value_formatter = EntityValueFormatter(
                name=data_source
            )

            #Register field names to be used for file naming
            self._file_name_value_formatter.register_columns(dataFields)

            #TODO: Need to automatically register custom configuration collections
            #Photo config collection
            ph_config_collection = PhotoConfigurationCollection.create(templateDoc)

            #Table configuration collection
            table_config_collection = TableConfigurationCollection.create(templateDoc)

            #Create chart configuration collection object
            chart_config_collection = ChartConfigurationCollection.create(templateDoc)

            # Create QR code configuration collection object
            qrc_config_collection = QRCodeConfigurationCollection.create(templateDoc)

            #Load the layers required by the table composer items
            self._table_mem_layers = load_table_layers(table_config_collection)

            entityFieldName = self.format_entity_field_name(composerDS.name(), data_source)

            #Execute query
            dsTable,records = self._exec_query(composerDS.name(), entityFieldName, entityFieldValue)

            if records is None or len(records) == 0:
                return False, QApplication.translate("DocumentGenerator",
                                                    "No matching records in the database")

            """
            Iterate through records where a single file output will be generated for each matching record.
            """

            for rec in records:
                composition = QgsPrintLayout(self._map_settings)
                composition.loadFromTemplate(templateDoc)
                ref_layer = None
                #Set value of composer items based on the corresponding db values
                for composerId in composerDS.dataFieldMappings().reverse:
                    #Use composer item id since the uuid is stripped off
                    composerItem = composition.getComposerItemById(composerId)
                    if not composerItem is None:
                        fieldName = composerDS.dataFieldName(composerId)
                        fieldValue = getattr(rec,fieldName)
                        self._composeritem_value_handler(composerItem, fieldValue)

                # Extract photo information
                self._extract_photo_info(composition, ph_config_collection, rec)

                # Set table item values based on configuration information
                self._set_table_data(composition, table_config_collection, rec)

                # Refresh non-custom map composer items
                self._refresh_composer_maps(composition,
                                            list(spatialFieldsConfig.spatialFieldsMapping().keys()))

                # Set use fixed scale to false i.e. relative zoom
                use_fixed_scale = False

                # Create memory layers for spatial features and add them to the map
                for mapId,spfmList in spatialFieldsConfig.spatialFieldsMapping().items():

                    map_item = composition.getComposerItemById(mapId)

                    if not map_item is None:
                        # Clear any previous map memory layer
                        # self.clear_temporary_map_layers()
                        for spfm in spfmList:
                            #Use the value of the label field to name the layer
                            lbl_field = spfm.labelField()
                            spatial_field = spfm.spatialField()

                            if not spatial_field:
                                continue

                            if lbl_field:
                                if hasattr(rec, spfm.labelField()):
                                    layerName = getattr(rec, spfm.labelField())
                                else:
                                    layerName = self._random_feature_layer_name(spatial_field)
                            else:
                                layerName = self._random_feature_layer_name(spatial_field)

                            #Extract the geometry using geoalchemy spatial capabilities
                            geom_value = getattr(rec, spatial_field)
                            if geom_value is None:
                                continue

                            geom_func = geom_value.ST_AsText()
                            geomWKT = self._dbSession.scalar(geom_func)

                            #Get geometry type
                            geom_type, srid = geometryType(composerDS.name(),
                                                          spatial_field)

                            #Create reference layer with feature
                            ref_layer = self._build_vector_layer(layerName, geom_type, srid)

                            if ref_layer is None or not ref_layer.isValid():
                                continue
                            # Add feature
                            bbox = self._add_feature_to_layer(ref_layer, geomWKT)

                            zoom_type = spfm.zoom_type
                            # Only scale the extents if zoom type is relative
                            if zoom_type == 'RELATIVE':
                                bbox.scale(spfm.zoomLevel())

                            #Workaround for zooming to single point extent
                            if ref_layer.wkbType() == QgsWkbTypes.Point:
                                canvas_extent = self._iface.mapCanvas().fullExtent()
                                cnt_pnt = bbox.center()
                                canvas_extent.scale(1.0/32, cnt_pnt)
                                bbox = canvas_extent

                            #Style layer based on the spatial field mapping symbol layer
                            symbol_layer = spfm.symbolLayer()
                            if not symbol_layer is None:
                                ref_layer.rendererV2().symbols()[0].changeSymbolLayer(0,spfm.symbolLayer())
                            '''
                            Add layer to map and ensure its always added at the top
                            '''
                            self.map_registry.addMapLayer(ref_layer)
                            self._iface.mapCanvas().setExtent(bbox)

                            # Set scale if type is FIXED
                            if zoom_type == 'FIXED':
                                self._iface.mapCanvas().zoomScale(spfm.zoomLevel())
                                use_fixed_scale = True

                            self._iface.mapCanvas().refresh()
                            # Add layer to map memory layer list
                            self._map_memory_layers.append(ref_layer.id())
                            self._hide_layer(ref_layer)
                        '''
                        Use root layer tree to get the correct ordering of layers
                        in the legend
                        '''
                        self._refresh_map_item(map_item, use_fixed_scale)

                # Extract chart information and generate chart
                self._generate_charts(composition, chart_config_collection, rec)

                # Extract QR code information in order to generate QR codes
                self._generate_qr_codes(composition, qrc_config_collection,rec)

                #Build output path and generate composition
                if not filePath is None and len(dataFields) == 0:
                    self._write_output(composition, outputMode, filePath)

                elif filePath is None and len(dataFields) > 0:
                    entityFieldName = 'id'
                    docFileName = self._build_file_name(data_source, entityFieldName,
                                                      entityFieldValue, dataFields, fileExtension)

                    # Replace unsupported characters in Windows file naming
                    docFileName = docFileName.replace('/', '_').replace \
                        ('\\', '_').replace(':', '_').strip('*?"<>|')


                    if not docFileName:
                        return (False, QApplication.translate("DocumentGenerator",
                                    "File name could not be generated from the data fields."))

                    outputDir = self._composer_output_path()
                    if outputDir is None:
                        return (False, QApplication.translate("DocumentGenerator",
                            "System could not read the location of the output directory in the registry."))

                    qDir = QDir()
                    if not qDir.exists(outputDir):
                        return (False, QApplication.translate("DocumentGenerator",
                                "Output directory does not exist"))

                    absDocPath = "{0}/{1}".format(outputDir, docFileName)
                    self._write_output(composition, outputMode, absDocPath)

            return True, "Success"

        return False, "Document composition could not be generated"
예제 #19
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

    Render using qgis composer for a given impact_report data and component
    context.

    :param impact_report: ImpactReport contains data about the report that is
        going to be generated.
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component: Contains the component metadata and context for
        rendering the output.
    :type component:
        safe.report.report_metadata.QgisComposerComponentsMetadata

    :return: Whatever type of output the component should be.

    .. versionadded:: 4.0
    """
    context = component.context
    qgis_composition_context = impact_report.qgis_composition_context

    # load composition object
    layout = QgsPrintLayout(QgsProject.instance())

    # load template
    main_template_folder = impact_report.metadata.template_folder

    # we do this condition in case custom template was found
    if component.template.startswith('../qgis-composer-templates/'):
        template_path = os.path.join(main_template_folder, component.template)
    else:
        template_path = component.template

    with open(template_path) as template_file:
        template_content = template_file.read()

    document = QtXml.QDomDocument()

    # Replace
    for k, v in context.substitution_map.items():
        template_content = template_content.replace('[{}]'.format(k), v)

    document.setContent(template_content)

    rwcontext = QgsReadWriteContext()
    load_status = layout.loadFromTemplate(
        document, rwcontext)

    if not load_status:
        raise TemplateLoadingError(
            tr('Error loading template: %s') % template_path)

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = layout_item(layout, item_id, QgsLayoutItemPicture)
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = layout_item(layout, item_id, QgsLayoutItemHtml)
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsLayoutItemHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsLayoutItemHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    original_crs = impact_report.impact_function.crs
    destination_crs = qgis_composition_context.map_settings.destinationCrs()
    coord_transform = QgsCoordinateTransform(original_crs,
                                             destination_crs,
                                             QgsProject.instance())

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = [
            _layer for _layer in map_el.get('layers') if isinstance(
                _layer, QgsMapLayer)
        ]
        map_extent_option = map_el.get('extent')
        composer_map = layout_item(layout, item_id, QgsLayoutItemMap)

        for index, _layer in enumerate(layers):
            # we need to check whether the layer is registered or not
            registered_layer = (
                QgsProject.instance().mapLayer(_layer.id()))
            if registered_layer:
                if not registered_layer == _layer:
                    layers[index] = registered_layer
            else:
                QgsProject.instance().addMapLayer(_layer)

        """:type: qgis.core.QgsLayoutItemMap"""
        if composer_map:

            # Search for specified map extent in the template.
            min_x = composer_map.extent().xMinimum() if (
                impact_report.use_template_extent) else None
            min_y = composer_map.extent().yMinimum() if (
                impact_report.use_template_extent) else None
            max_x = composer_map.extent().xMaximum() if (
                impact_report.use_template_extent) else None
            max_y = composer_map.extent().yMaximum() if (
                impact_report.use_template_extent) else None

            composer_map.setKeepLayerSet(True)
            layer_set = [_layer for _layer in layers if isinstance(
                _layer, QgsMapLayer)]
            composer_map.setLayers(layer_set)
            map_overview_extent = None
            if map_extent_option and isinstance(
                    map_extent_option, QgsRectangle):
                # use provided map extent
                extent = coord_transform.transform(map_extent_option)
                for layer in layer_set:
                    layer_extent = coord_transform.transform(layer.extent())
                    if layer.name() == map_overview['id']:
                        map_overview_extent = layer_extent
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for layer in layer_set:
                    # combine extent if different layer is provided.
                    layer_extent = coord_transform.transform(layer.extent())
                    extent.combineExtentWith(layer_extent)
                    if layer.name() == map_overview['id']:
                        map_overview_extent = layer_extent

            width = extent.width()
            height = extent.height()
            longest_width = width if width > height else height
            half_length = longest_width / 2
            margin = half_length / 5
            center = extent.center()
            min_x = min_x or (center.x() - half_length - margin)
            max_x = max_x or (center.x() + half_length + margin)
            min_y = min_y or (center.y() - half_length - margin)
            max_y = max_y or (center.y() + half_length + margin)

            # noinspection PyCallingNonCallable
            square_extent = QgsRectangle(min_x, min_y, max_x, max_y)

            if component.key == 'population-infographic' and (
                    map_overview_extent):
                square_extent = map_overview_extent

            composer_map.zoomToExtent(square_extent)
            composer_map.invalidateCache()

            actual_extent = composer_map.extent()

            # calculate intervals for grid
            x_interval = actual_extent.width() / split_count
            composer_map.grid().setIntervalX(x_interval)
            y_interval = actual_extent.height() / split_count
            composer_map.grid().setIntervalY(y_interval)

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = [
            _layer for _layer in leg_el.get('layers') if isinstance(
                _layer, QgsMapLayer)
        ]
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = layout_item(layout, item_id, QgsLayoutItemLegend)
        """:type: qgis.core.QgsLayoutItemLegend"""
        if legend:
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 7:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 7 + 1)

            # set legend title
            if title is not None and not impact_report.legend_layers:
                legend.setTitle(title)

            # set legend
            root_group = legend.model().rootGroup()
            for _layer in layers:
                # we need to check whether the layer is registered or not
                registered_layer = (
                    QgsProject.instance().mapLayer(_layer.id()))
                if registered_layer:
                    if not registered_layer == _layer:
                        _layer = registered_layer
                else:
                    QgsProject.instance().addMapLayer(_layer)
                # used for customizations
                tree_layer = root_group.addLayer(_layer)
                if impact_report.legend_layers or (
                        not impact_report.multi_exposure_impact_function):
                    QgsLegendRenderer.setNodeLegendStyle(
                        tree_layer, QgsLegendStyle.Hidden)
            legend.adjustBoxSize()
            legend.updateFilterByMap(False)

    # process to output

    # in case output folder not specified
    if impact_report.output_folder is None:
        impact_report.output_folder = mkdtemp(dir=temp_dir())

    output_format = component.output_format
    component_output_path = impact_report.component_absolute_output_path(
        component.key)
    component_output = None

    doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT
    template_format = QgisComposerComponentsMetadata.OutputFormat.QPT
    if isinstance(output_format, list):
        component_output = []
        for i in range(len(output_format)):
            each_format = output_format[i]
            each_path = component_output_path[i]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output.append(result_path)
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output.append(result_path)
    elif isinstance(output_format, dict):
        component_output = {}
        for key, each_format in list(output_format.items()):
            each_path = component_output_path[key]

            if each_format in doc_format:
                result_path = create_qgis_pdf_output(
                    impact_report,
                    each_path,
                    layout,
                    each_format,
                    component)
                component_output[key] = result_path
            elif each_format == template_format:
                result_path = create_qgis_template_output(
                    each_path, layout)
                component_output[key] = result_path
    elif (output_format in
            QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT):
        component_output = None

        if output_format in doc_format:
            result_path = create_qgis_pdf_output(
                impact_report,
                component_output_path,
                layout,
                output_format,
                component)
            component_output = result_path
        elif output_format == template_format:
            result_path = create_qgis_template_output(
                component_output_path, layout)
            component_output = result_path

    component.output = component_output

    return component.output