Example #1
0
    def testCasting(self):
        """
        Test that sip correctly casts stuff
        """
        p = QgsProject()
        p.read(os.path.join(TEST_DATA_DIR, 'layouts', 'layout_casting.qgs'))

        layout = p.layoutManager().layouts()[0]

        # check a method which often fails casting
        map = layout.itemById('map')
        self.assertIsInstance(map, QgsLayoutItemMap)
        label = layout.itemById('label')
        self.assertIsInstance(label, QgsLayoutItemLabel)

        # another method -- sometimes this fails casting for different(?) reasons
        # make sure we start from a new project so sip hasn't remembered item instances
        p2 = QgsProject()
        p2.read(os.path.join(TEST_DATA_DIR, 'layouts', 'layout_casting.qgs'))
        layout = p2.layoutManager().layouts()[0]

        items = layout.items()
        map2 = [
            i for i in items
            if isinstance(i, QgsLayoutItem) and i.id() == 'map'
        ][0]
        self.assertIsInstance(map2, QgsLayoutItemMap)
        label2 = [
            i for i in items
            if isinstance(i, QgsLayoutItem) and i.id() == 'label'
        ][0]
        self.assertIsInstance(label2, QgsLayoutItemLabel)
Example #2
0
    def testCasting(self):
        """
        Test that sip correctly casts stuff
        """
        p = QgsProject()
        p.read(os.path.join(TEST_DATA_DIR, 'layouts', 'layout_casting.qgs'))

        layout = p.layoutManager().layouts()[0]

        # check a method which often fails casting
        map = layout.itemById('map')
        self.assertIsInstance(map, QgsLayoutItemMap)
        label = layout.itemById('label')
        self.assertIsInstance(label, QgsLayoutItemLabel)

        # another method -- sometimes this fails casting for different(?) reasons
        # make sure we start from a new project so sip hasn't remembered item instances
        p2 = QgsProject()
        p2.read(os.path.join(TEST_DATA_DIR, 'layouts', 'layout_casting.qgs'))
        layout = p2.layoutManager().layouts()[0]

        items = layout.items()
        map2 = [i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'map'][0]
        self.assertIsInstance(map2, QgsLayoutItemMap)
        label2 = [i for i in items if isinstance(i, QgsLayoutItem) and i.id() == 'label'][0]
        self.assertIsInstance(label2, QgsLayoutItemLabel)
Example #3
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """

        # get the parameter values
        base_project = self.parameterAsFile(parameters, self.BASE_PROJECT, context)
        layout_name = self.parameterAsString(parameters, self.LAYOUT_NAME, context)
        output = self.parameterAsFile(parameters, self.OUTPUT, context)

        # instantiation of the project
        project_instance = QgsProject()
        project_instance.read(base_project)

        # actual export of the PDF
        if layout_name:
            layout = project_instance.layoutManager().layoutByName(layout_name)
        else:
            layout = project_instance.layoutManager().layouts()[0]
        assert(layout is not None, "No layout found")
        export = QgsLayoutExporter(layout)
        export.exportToPdf(output, QgsLayoutExporter.PdfExportSettings())

        # debug
        # project_instance.write(output_file+"debug.qgs")

        # done !!
        return {}
    def testSaveRestore(self):
        p = QgsProject()
        l = QgsPrintLayout(p)
        p.layoutManager().addLayout(l)
        map = QgsLayoutItemMap(l)
        shape = QgsLayoutItemShape(l)
        l.addLayoutItem(map)
        l.addLayoutItem(shape)

        settings = map.itemClippingSettings()
        settings.setEnabled(True)
        settings.setFeatureClippingType(
            QgsMapClippingRegion.FeatureClippingType.NoClipping)
        settings.setForceLabelsInsideClipPath(True)
        settings.setSourceItem(shape)

        # save map to xml
        doc = QDomDocument("testdoc")
        elem = doc.createElement("test")
        self.assertTrue(map.writeXml(elem, doc, QgsReadWriteContext()))
        elem_shape = doc.createElement("shape")
        self.assertTrue(shape.writeXml(elem_shape, doc, QgsReadWriteContext()))

        layout2 = QgsPrintLayout(p)
        layout2.setName('test2')
        p.layoutManager().addLayout(layout2)
        map2 = QgsLayoutItemMap(layout2)
        layout2.addLayoutItem(map2)
        shape2 = QgsLayoutItemShape(layout2)
        layout2.addLayoutItem(shape2)

        self.assertFalse(map2.itemClippingSettings().enabled())

        # restore from xml
        self.assertTrue(
            map2.readXml(elem.firstChildElement(), doc, QgsReadWriteContext()))
        self.assertTrue(
            shape2.readXml(elem_shape.firstChildElement(), doc,
                           QgsReadWriteContext()))

        self.assertTrue(map2.itemClippingSettings().enabled())
        self.assertEqual(map2.itemClippingSettings().featureClippingType(),
                         QgsMapClippingRegion.FeatureClippingType.NoClipping)
        self.assertTrue(
            map2.itemClippingSettings().forceLabelsInsideClipPath())
        self.assertIsNone(map2.itemClippingSettings().sourceItem())

        map2.finalizeRestoreFromXml()

        self.assertEqual(map2.itemClippingSettings().sourceItem(), shape2)
    def testToMapClippingRegion(self):
        p = QgsProject()
        l = QgsPrintLayout(p)
        p.layoutManager().addLayout(l)
        map = QgsLayoutItemMap(l)
        shape = QgsLayoutItemShape(l)
        l.addLayoutItem(map)
        map.attemptSetSceneRect(QRectF(10, 20, 100, 80))
        map.zoomToExtent(QgsRectangle(100, 200, 50, 40))
        l.addLayoutItem(shape)
        shape.setShapeType(QgsLayoutItemShape.Triangle)
        shape.attemptSetSceneRect(QRectF(20, 30, 70, 50))

        settings = map.itemClippingSettings()
        settings.setEnabled(True)
        settings.setFeatureClippingType(
            QgsMapClippingRegion.FeatureClippingType.NoClipping)
        settings.setSourceItem(shape)

        region = settings.toMapClippingRegion()
        self.assertEqual(region.geometry().asWkt(),
                         'Polygon ((-5 80, 135 80, 65 180, -5 80))')
        self.assertEqual(region.featureClip(),
                         QgsMapClippingRegion.FeatureClippingType.NoClipping)
Example #6
0
def print_layout(
    project: QgsProject,
    layout_name: str,
    output_format: OutputFormat,
    feature_filter: str = None,
    scales: list = None,
    scale: int = None,
    **kwargs,
):
    """Generate a PDF for an atlas or a report.

    :param project: The QGIS project.
    :type project: QgsProject

    :param layout_name: Name of the layout of the atlas or report.
    :type layout_name: basestring

    :param feature_filter: QGIS Expression to use to select the feature.
    It can return many features, a multiple pages PDF will be returned.
    This is required to print atlas, not report
    :type feature_filter: basestring

    :param scale: A scale to force in the atlas context. Default to None.
    :type scale: int

    :param scales: A list of predefined list of scales to force in the atlas context.
    Default to None.
    :type scales: list

    :param output_format: The output format, default to PDF if not provided.

    :return: Path to the PDF.
    :rtype: basestring
    """
    canvas = QgsMapCanvas()
    bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas)
    bridge.setCanvasLayers()
    manager = project.layoutManager()
    master_layout = manager.layoutByName(layout_name)

    if output_format == OutputFormat.Svg:
        settings = QgsLayoutExporter.SvgExportSettings()
    elif output_format in (OutputFormat.Png, OutputFormat.Jpeg):
        settings = QgsLayoutExporter.ImageExportSettings()
    else:
        # PDF by default
        settings = QgsLayoutExporter.PdfExportSettings()

    atlas = None
    atlas_layout = None
    report_layout = None

    logger = Logger()

    if not master_layout:
        raise AtlasPrintException("Layout `{}` not found".format(layout_name))

    if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout:
        for _print_layout in manager.printLayouts():
            if _print_layout.name() == layout_name:
                atlas_layout = _print_layout
                break

        atlas = atlas_layout.atlas()
        if not atlas.enabled():
            raise AtlasPrintException("The layout is not enabled for an atlas")

        layer = atlas.coverageLayer()

        if feature_filter is None:
            raise AtlasPrintException(
                "EXP_FILTER is mandatory to print an atlas layout")

        feature_filter = optimize_expression(layer, feature_filter)

        expression = QgsExpression(feature_filter)
        if expression.hasParserError():
            raise AtlasPrintException(
                "Expression is invalid, parser error: {}".format(
                    expression.parserErrorString()))

        context = QgsExpressionContext()
        context.appendScope(QgsExpressionContextUtils.globalScope())
        context.appendScope(QgsExpressionContextUtils.projectScope(project))
        context.appendScope(
            QgsExpressionContextUtils.layoutScope(atlas_layout))
        context.appendScope(QgsExpressionContextUtils.atlasScope(atlas))
        context.appendScope(QgsExpressionContextUtils.layerScope(layer))
        expression.prepare(context)
        if expression.hasEvalError():
            raise AtlasPrintException(
                "Expression is invalid, eval error: {}".format(
                    expression.evalErrorString()))

        atlas.setFilterFeatures(True)
        atlas.setFilterExpression(feature_filter)
        atlas.updateFeatures()

        if scale:
            atlas_layout.referenceMap().setAtlasScalingMode(
                QgsLayoutItemMap.Fixed)
            atlas_layout.referenceMap().setScale(scale)

        if scales:
            atlas_layout.referenceMap().setAtlasScalingMode(
                QgsLayoutItemMap.Predefined)
            settings.predefinedMapScales = scales

        if (not scales and atlas_layout.referenceMap().atlasScalingMode()
                == QgsLayoutItemMap.Predefined):
            use_project = project.useProjectScales()
            map_scales = project.mapScales()
            if not use_project or len(map_scales) == 0:
                logger.info(
                    "Map scales not found in project, fetching predefined map scales in global config"
                )
                map_scales = global_scales()
            settings.predefinedMapScales = map_scales

    elif master_layout.layoutType() == QgsMasterLayoutInterface.Report:
        report_layout = master_layout

    else:
        raise AtlasPrintException("The layout is not supported by the plugin")

    for key, value in kwargs.items():
        found = False
        if atlas_layout:
            item = atlas_layout.itemById(key.lower())
            if isinstance(item, QgsLayoutItemLabel):
                item.setText(value)
                found = True
        logger.info(
            'Additional parameters "{key}" {found} in layout, value "{value}"'.
            format(key=key,
                   found="found" if found else "not found",
                   value=value))

    file_name = "{}_{}.{}".format(clean_string(layout_name), uuid4(),
                                  output_format.name.lower())
    export_path = Path(tempfile.gettempdir()).joinpath(file_name)

    Logger().info("Exporting the request in {} using {}".format(
        export_path, output_format.value))

    if output_format in (OutputFormat.Png, OutputFormat.Jpeg):
        exporter = QgsLayoutExporter(atlas_layout or report_layout)
        result = exporter.exportToImage(str(export_path), settings)
        error = result_message(result)
    elif output_format in (OutputFormat.Svg, ):
        exporter = QgsLayoutExporter(atlas_layout or report_layout)
        result = exporter.exportToSvg(str(export_path), settings)
        error = result_message(result)
    else:
        # Default to PDF
        result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout,
                                                      str(export_path),
                                                      settings)
        # Let's override error message
        _ = error
        error = result_message(result)

    if result != QgsLayoutExporter.Success:
        raise Exception("Export not generated in QGIS exporter {} : {}".format(
            export_path, error))

    if not export_path.is_file():
        logger.warning(
            "No error from QGIS Exporter, but the file does not exist.\n"
            "Message from QGIS exporter : {}\n"
            "File path : {}\n".format(error, export_path))
        raise Exception(
            "Export OK from QGIS, but file not found on the file system : {}".
            format(export_path))

    return export_path