Пример #1
0
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     crs = QgsCoordinateReferenceSystem()
     """:type: QgsCoordinateReferenceSystem"""
     # default for labeling test data: WGS 84 / UTM zone 13N
     crs.createFromSrid(32613)
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing)
     ms.setDestinationCrs(crs)
     ms.setCrsTransformEnabled(False)
     ms.setMapUnits(crs.mapUnits())  # meters
     ms.setExtent(cls.aoiExtent())
     return ms
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     crs = QgsCoordinateReferenceSystem()
     """:type: QgsCoordinateReferenceSystem"""
     crs.createFromSrid(4326)
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing, True)
     ms.setFlag(QgsMapSettings.UseAdvancedEffects, False)
     ms.setFlag(QgsMapSettings.ForceVectorOutput, False)  # no caching?
     ms.setDestinationCrs(crs)
     return ms
Пример #3
0
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     crs = QgsCoordinateReferenceSystem()
     """:type: QgsCoordinateReferenceSystem"""
     crs.createFromSrid(4326)
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing, True)
     ms.setFlag(QgsMapSettings.UseAdvancedEffects, False)
     ms.setFlag(QgsMapSettings.ForceVectorOutput, False)  # no caching?
     ms.setDestinationCrs(crs)
     return ms
Пример #4
0
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     # default for labeling test data: WGS 84 / UTM zone 13N
     crs = QgsCoordinateReferenceSystem('epsg:32613')
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing, True)
     ms.setFlag(QgsMapSettings.UseAdvancedEffects, False)
     ms.setFlag(QgsMapSettings.ForceVectorOutput, False)  # no caching?
     ms.setDestinationCrs(crs)
     ms.setExtent(cls.aoiExtent())
     return ms
Пример #5
0
    def testFromMapSettings(self):
        """
        test QgsRenderContext.fromMapSettings()
        """
        ms = QgsMapSettings()
        ms.setOutputSize(QSize(1000, 1000))
        ms.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
        ms.setExtent(QgsRectangle(10000, 20000, 30000, 40000))
        ms.setFlag(QgsMapSettings.Antialiasing, True)
        ms.setFlag(QgsMapSettings.LosslessImageRendering, True)
        ms.setFlag(QgsMapSettings.Render3DMap, True)
        ms.setZRange(QgsDoubleRange(1, 10))

        ms.setTextRenderFormat(QgsRenderContext.TextFormatAlwaysText)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.textRenderFormat(), QgsRenderContext.TextFormatAlwaysText)
        self.assertTrue(rc.testFlag(QgsRenderContext.Antialiasing))
        self.assertTrue(rc.testFlag(QgsRenderContext.LosslessImageRendering))
        self.assertTrue(rc.testFlag(QgsRenderContext.Render3DMap))
        self.assertEqual(ms.zRange(), QgsDoubleRange(1, 10))
        self.assertEqual(rc.symbologyReferenceScale(), -1)

        # should have an valid mapToPixel
        self.assertTrue(rc.mapToPixel().isValid())

        ms.setTextRenderFormat(QgsRenderContext.TextFormatAlwaysOutlines)
        ms.setZRange(QgsDoubleRange())
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.textRenderFormat(), QgsRenderContext.TextFormatAlwaysOutlines)
        self.assertTrue(ms.zRange().isInfinite())

        self.assertEqual(rc.mapExtent(), QgsRectangle(10000, 20000, 30000, 40000))

        ms.setIsTemporal(True)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.isTemporal(), True)

        ms.setTemporalRange(QgsDateTimeRange(QDateTime(2020, 1, 1, 0, 0), QDateTime(2010, 12, 31, 23, 59)))
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.temporalRange(),
                         QgsDateTimeRange(QDateTime(2020, 1, 1, 0, 0), QDateTime(2010, 12, 31, 23, 59)))

        ms.setDpiTarget(111.1)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.dpiTarget(), 111.1)
Пример #6
0
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     crs = QgsCoordinateReferenceSystem()
     """:type: QgsCoordinateReferenceSystem"""
     # default for labeling test data: WGS 84 / UTM zone 13N
     crs.createFromSrid(32613)
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing, True)
     ms.setFlag(QgsMapSettings.UseAdvancedEffects, False)
     ms.setFlag(QgsMapSettings.ForceVectorOutput, False)  # no caching?
     ms.setDestinationCrs(crs)
     ms.setExtent(cls.aoiExtent())
     return ms
Пример #7
0
 def getBaseMapSettings(cls):
     """
     :rtype: QgsMapSettings
     """
     ms = QgsMapSettings()
     crs = QgsCoordinateReferenceSystem()
     """:type: QgsCoordinateReferenceSystem"""
     # default for labeling test data: WGS 84 / UTM zone 13N
     crs.createFromSrid(32613)
     ms.setBackgroundColor(QColor(152, 219, 249))
     ms.setOutputSize(QSize(420, 280))
     ms.setOutputDpi(72)
     ms.setFlag(QgsMapSettings.Antialiasing, True)
     ms.setFlag(QgsMapSettings.UseAdvancedEffects, False)
     ms.setFlag(QgsMapSettings.ForceVectorOutput, False)  # no caching?
     ms.setDestinationCrs(crs)
     ms.setCrsTransformEnabled(False)
     ms.setMapUnits(crs.mapUnits())  # meters
     ms.setExtent(cls.aoiExtent())
     return ms
Пример #8
0
    def testFromMapSettings(self):
        """
        test QgsRenderContext.fromMapSettings()
        """
        ms = QgsMapSettings()
        ms.setOutputSize(QSize(1000, 1000))
        ms.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
        ms.setExtent(QgsRectangle(10000, 20000, 30000, 40000))
        ms.setFlag(QgsMapSettings.Antialiasing, True)
        ms.setFlag(QgsMapSettings.LosslessImageRendering, True)
        ms.setFlag(QgsMapSettings.Render3DMap, True)

        ms.setTextRenderFormat(QgsRenderContext.TextFormatAlwaysText)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.textRenderFormat(),
                         QgsRenderContext.TextFormatAlwaysText)
        self.assertTrue(rc.testFlag(QgsRenderContext.Antialiasing))
        self.assertTrue(rc.testFlag(QgsRenderContext.LosslessImageRendering))
        self.assertTrue(rc.testFlag(QgsRenderContext.Render3DMap))

        ms.setTextRenderFormat(QgsRenderContext.TextFormatAlwaysOutlines)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.textRenderFormat(),
                         QgsRenderContext.TextFormatAlwaysOutlines)

        self.assertEqual(rc.mapExtent(),
                         QgsRectangle(10000, 20000, 30000, 40000))

        ms.setIsTemporal(True)
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(rc.isTemporal(), True)

        ms.setTemporalRange(
            QgsDateTimeRange(QDateTime(2020, 1, 1, 0, 0),
                             QDateTime(2010, 12, 31, 23, 59)))
        rc = QgsRenderContext.fromMapSettings(ms)
        self.assertEqual(
            rc.temporalRange(),
            QgsDateTimeRange(QDateTime(2020, 1, 1, 0, 0),
                             QDateTime(2010, 12, 31, 23, 59)))
Пример #9
0
class TileSet():

    """
    A set of tiles
    """

    def __init__(self, map_theme, layer, extent, tile_size, mupp, output,
                 make_trans, map_settings):
        """
        :param map_theme:
        :param extent:
        :param layer:
        :param tile_size:
        :param mupp:
        :param output:
        :param map_settings: Map canvas map settings used for some fallback
        values and CRS
        """

        self.extent = extent
        self.mupp = mupp
        self.tile_size = tile_size

        driver = self.getDriverForFile(output)

        if not driver:
            raise QgsProcessingException(
                u'Could not load GDAL driver for file {}'.format(output))

        crs = map_settings.destinationCrs()

        self.x_tile_count = math.ceil(extent.width() / mupp / tile_size)
        self.y_tile_count = math.ceil(extent.height() / mupp / tile_size)

        xsize = self.x_tile_count * tile_size
        ysize = self.y_tile_count * tile_size

        if make_trans:
            no_bands = 4
        else:
            no_bands = 3

        self.dataset = driver.Create(output, xsize, ysize, no_bands)
        self.dataset.SetProjection(str(crs.toWkt()))
        self.dataset.SetGeoTransform(
            [extent.xMinimum(), mupp, 0, extent.yMaximum(), 0, -mupp])

        self.image = QImage(QSize(tile_size, tile_size), QImage.Format_ARGB32)

        self.settings = QgsMapSettings()
        self.settings.setOutputDpi(self.image.logicalDpiX())
        self.settings.setOutputImageFormat(QImage.Format_ARGB32)
        self.settings.setDestinationCrs(crs)
        self.settings.setOutputSize(self.image.size())
        self.settings.setFlag(QgsMapSettings.Antialiasing, True)
        self.settings.setFlag(QgsMapSettings.RenderMapTile, True)
        self.settings.setFlag(QgsMapSettings.UseAdvancedEffects, True)

        if make_trans:
            self.settings.setBackgroundColor(QColor(255, 255, 255, 0))
        else:
            self.settings.setBackgroundColor(QColor(255, 255, 255))

        if QgsProject.instance().mapThemeCollection().hasMapTheme(map_theme):
            self.settings.setLayers(
                QgsProject.instance().mapThemeCollection(

                ).mapThemeVisibleLayers(
                    map_theme))
            self.settings.setLayerStyleOverrides(
                QgsProject.instance().mapThemeCollection(

                ).mapThemeStyleOverrides(
                    map_theme))
        elif layer:
            self.settings.setLayers([layer])
        else:
            self.settings.setLayers(map_settings.layers())

    def render(self, feedback, make_trans):
        for x in range(self.x_tile_count):
            for y in range(self.y_tile_count):
                if feedback.isCanceled():
                    return
                cur_tile = x * self.y_tile_count + y
                num_tiles = self.x_tile_count * self.y_tile_count
                self.renderTile(x, y, feedback, make_trans)

                feedback.setProgress(int((cur_tile / num_tiles) * 100))

    def renderTile(self, x, y, feedback, make_trans):
        """
        Render one tile
        :param x: The x index of the current tile
        :param y: The y index of the current tile
        """

        if make_trans:
            background_color = QColor(255, 255, 255, 0)
            self.image.fill(background_color.rgba())
        else:
            background_color = QColor(255, 255, 255)
            self.image.fill(background_color.rgb())

        painter = QPainter(self.image)

        self.settings.setExtent(QgsRectangle(
            self.extent.xMinimum() + x * self.mupp * self.tile_size,
            self.extent.yMaximum() - (y + 1) * self.mupp * self.tile_size,
            self.extent.xMinimum() + (x + 1) * self.mupp * self.tile_size,
            self.extent.yMaximum() - y * self.mupp * self.tile_size))

        job = QgsMapRendererCustomPainterJob(self.settings, painter)
        job.renderSynchronously()
        painter.end()

        # Needs not to be deleted or Windows will kill it too early...
        tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
        try:
            self.image.save(tmpfile.name)

            src_ds = osgeo.gdal.Open(tmpfile.name)

            self.dataset.WriteRaster(x * self.tile_size, y * self.tile_size,
                                     self.tile_size, self.tile_size,
                                     src_ds.ReadRaster(0, 0, self.tile_size,
                                                       self.tile_size))
        except Exception as e:
            feedback.reportError(str(e))
        finally:
            del src_ds
            tmpfile.close()
            os.unlink(tmpfile.name)

    def getDriverForFile(self, filename):
        """
        Get the GDAL driver for a filename, based on its extension. (.gpkg,
        .mbtiles...)
        """
        _, extension = os.path.splitext(filename)

        # If no extension is set, use .tif as default
        if extension == '':
            extension = '.tif'

        driver_name = QgsRasterFileWriter.driverForExtension(extension[1:])
        return osgeo.gdal.GetDriverByName(driver_name)
Пример #10
0
    def testRenderWithTransform(self):
        layer = QgsAnnotationLayer(
            'test',
            QgsAnnotationLayer.LayerOptions(
                QgsProject.instance().transformContext()))
        self.assertTrue(layer.isValid())

        item = QgsAnnotationPolygonItem(
            QgsPolygon(
                QgsLineString([
                    QgsPoint(11.5, 13),
                    QgsPoint(12, 13),
                    QgsPoint(12, 13.5),
                    QgsPoint(11.5, 13)
                ])))
        item.setSymbol(
            QgsFillSymbol.createSimple({
                'color': '200,100,100',
                'outline_color': 'black',
                'outline_width': '2'
            }))
        item.setZIndex(1)
        layer.addItem(item)

        item = QgsAnnotationLineItem(
            QgsLineString(
                [QgsPoint(11, 13),
                 QgsPoint(12, 13),
                 QgsPoint(12, 15)]))
        item.setSymbol(
            QgsLineSymbol.createSimple({
                'color': '#ffff00',
                'line_width': '3'
            }))
        item.setZIndex(2)
        layer.addItem(item)

        item = QgsAnnotationMarkerItem(QgsPoint(12, 13))
        item.setSymbol(
            QgsMarkerSymbol.createSimple({
                'color': '100,200,200',
                'size': '6',
                'outline_color': 'black'
            }))
        item.setZIndex(3)
        layer.addItem(item)

        layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))

        settings = QgsMapSettings()
        settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
        settings.setExtent(QgsRectangle(1250958, 1386945, 1420709, 1532518))
        settings.setOutputSize(QSize(300, 300))

        settings.setFlag(QgsMapSettings.Antialiasing, False)

        rc = QgsRenderContext.fromMapSettings(settings)
        rc.setCoordinateTransform(
            QgsCoordinateTransform(layer.crs(), settings.destinationCrs(),
                                   QgsProject.instance()))
        image = QImage(200, 200, QImage.Format_ARGB32)
        image.setDotsPerMeterX(96 / 25.4 * 1000)
        image.setDotsPerMeterY(96 / 25.4 * 1000)
        image.fill(QColor(255, 255, 255))
        painter = QPainter(image)
        rc.setPainter(painter)

        try:
            renderer = layer.createMapRenderer(rc)
            renderer.render()
        finally:
            painter.end()

        self.assertTrue(
            self.imageCheck('layer_render_transform', 'layer_render_transform',
                            image))
Пример #11
0
    def testRenderLayerWithReferenceScale(self):
        layer = QgsAnnotationLayer(
            'test',
            QgsAnnotationLayer.LayerOptions(
                QgsProject.instance().transformContext()))
        layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        self.assertTrue(layer.isValid())

        item = QgsAnnotationPolygonItem(
            QgsPolygon(
                QgsLineString([
                    QgsPoint(12, 13),
                    QgsPoint(14, 13),
                    QgsPoint(14, 15),
                    QgsPoint(12, 13)
                ])))
        item.setSymbol(
            QgsFillSymbol.createSimple({
                'color': '200,100,100',
                'outline_color': 'black',
                'outline_width': '2'
            }))
        item.setZIndex(3)
        i1_id = layer.addItem(item)

        item = QgsAnnotationLineItem(
            QgsLineString(
                [QgsPoint(11, 13),
                 QgsPoint(12, 13),
                 QgsPoint(12, 15)]))
        item.setSymbol(
            QgsLineSymbol.createSimple({
                'color': '#ffff00',
                'line_width': '3'
            }))
        item.setZIndex(2)
        i2_id = layer.addItem(item)

        item = QgsAnnotationMarkerItem(QgsPoint(12, 13))
        item.setSymbol(
            QgsMarkerSymbol.createSimple({
                'color': '100,200,200',
                'size': '6',
                'outline_color': 'black'
            }))
        item.setZIndex(1)
        i3_id = layer.addItem(item)

        settings = QgsMapSettings()
        settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        settings.setExtent(QgsRectangle(10, 10, 18, 18))
        settings.setOutputSize(QSize(300, 300))

        settings.setFlag(QgsMapSettings.Antialiasing, False)

        rc = QgsRenderContext.fromMapSettings(settings)

        layer.item(i1_id).setUseSymbologyReferenceScale(True)
        layer.item(i1_id).setSymbologyReferenceScale(rc.rendererScale() * 2)
        # note item 3 has use symbology reference scale set to false, so should be ignored
        layer.item(i2_id).setUseSymbologyReferenceScale(False)
        layer.item(i2_id).setSymbologyReferenceScale(rc.rendererScale() * 2)
        layer.item(i3_id).setUseSymbologyReferenceScale(True)
        layer.item(i3_id).setSymbologyReferenceScale(rc.rendererScale() * 2)

        image = QImage(200, 200, QImage.Format_ARGB32)
        image.setDotsPerMeterX(int(96 / 25.4 * 1000))
        image.setDotsPerMeterY(int(96 / 25.4 * 1000))
        image.fill(QColor(255, 255, 255))
        painter = QPainter(image)
        rc.setPainter(painter)

        try:
            renderer = layer.createMapRenderer(rc)
            renderer.render()
        finally:
            painter.end()

        self.assertTrue(
            self.imageCheck('layer_render_reference_scale',
                            'layer_render_reference_scale', image))

        # also check details of rendered items
        item_details = renderer.takeRenderedItemDetails()
        self.assertEqual([i.layerId() for i in item_details], [layer.id()] * 3)
        self.assertCountEqual([i.itemId() for i in item_details],
                              [i1_id, i2_id, i3_id])
        self.assertEqual(
            [i.boundingBox() for i in item_details if i.itemId() == i1_id][0],
            QgsRectangle(12, 13, 14, 15))
        self.assertEqual(
            [i.boundingBox() for i in item_details if i.itemId() == i2_id][0],
            QgsRectangle(11, 13, 12, 15))
        self.assertEqual([
            i.boundingBox().toString(1) for i in item_details
            if i.itemId() == i3_id
        ][0], '11.4,12.4 : 12.6,13.6')
Пример #12
0
    def testRenderWithTransform(self):
        layer = QgsAnnotationLayer(
            'test',
            QgsAnnotationLayer.LayerOptions(
                QgsProject.instance().transformContext()))
        self.assertTrue(layer.isValid())

        item = QgsAnnotationPolygonItem(
            QgsPolygon(
                QgsLineString([
                    QgsPoint(11.5, 13),
                    QgsPoint(12, 13),
                    QgsPoint(12, 13.5),
                    QgsPoint(11.5, 13)
                ])))
        item.setSymbol(
            QgsFillSymbol.createSimple({
                'color': '200,100,100',
                'outline_color': 'black',
                'outline_width': '2'
            }))
        item.setZIndex(1)
        i1_id = layer.addItem(item)

        item = QgsAnnotationLineItem(
            QgsLineString(
                [QgsPoint(11, 13),
                 QgsPoint(12, 13),
                 QgsPoint(12, 15)]))
        item.setSymbol(
            QgsLineSymbol.createSimple({
                'color': '#ffff00',
                'line_width': '3'
            }))
        item.setZIndex(2)
        i2_id = layer.addItem(item)

        item = QgsAnnotationMarkerItem(QgsPoint(12, 13))
        item.setSymbol(
            QgsMarkerSymbol.createSimple({
                'color': '100,200,200',
                'size': '6',
                'outline_color': 'black'
            }))
        item.setZIndex(3)
        i3_id = layer.addItem(item)

        layer.setCrs(QgsCoordinateReferenceSystem('EPSG:4326'))

        settings = QgsMapSettings()
        settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
        settings.setExtent(QgsRectangle(1250958, 1386945, 1420709, 1532518))
        settings.setOutputSize(QSize(300, 300))

        settings.setFlag(QgsMapSettings.Antialiasing, False)

        rc = QgsRenderContext.fromMapSettings(settings)
        rc.setCoordinateTransform(
            QgsCoordinateTransform(layer.crs(), settings.destinationCrs(),
                                   QgsProject.instance()))
        rc.setExtent(rc.coordinateTransform().transformBoundingBox(
            settings.extent(), QgsCoordinateTransform.ReverseTransform))
        image = QImage(200, 200, QImage.Format_ARGB32)
        image.setDotsPerMeterX(int(96 / 25.4 * 1000))
        image.setDotsPerMeterY(int(96 / 25.4 * 1000))
        image.fill(QColor(255, 255, 255))
        painter = QPainter(image)
        rc.setPainter(painter)

        try:
            renderer = layer.createMapRenderer(rc)
            renderer.render()
        finally:
            painter.end()

        self.assertTrue(
            self.imageCheck('layer_render_transform', 'layer_render_transform',
                            image))

        # also check details of rendered items
        item_details = renderer.takeRenderedItemDetails()
        self.assertEqual([i.layerId() for i in item_details], [layer.id()] * 3)
        self.assertCountEqual([i.itemId() for i in item_details],
                              [i1_id, i2_id, i3_id])
        self.assertEqual(
            [i.boundingBox() for i in item_details if i.itemId() == i1_id][0],
            QgsRectangle(11.5, 13, 12, 13.5))
        self.assertEqual(
            [i.boundingBox() for i in item_details if i.itemId() == i2_id][0],
            QgsRectangle(11, 13, 12, 15))
        self.assertEqual([
            i.boundingBox().toString(2) for i in item_details
            if i.itemId() == i3_id
        ][0], '11.94,12.94 : 12.06,13.06')
Пример #13
0
class TileSet():
    """
    A set of tiles
    """
    def __init__(self, map_theme, layer, extent, tile_size, mupp, output,
                 map_settings):
        """

        :param map_theme:
        :param extent:
        :param layer:
        :param tile_size:
        :param mupp:
        :param output:
        :param map_settings: Map canvas map settings used for some fallback values and CRS
        """

        self.extent = extent
        self.mupp = mupp
        self.tile_size = tile_size

        # TODO: Check if file exists and update instead?
        driver = self.getDriverForFile(output)

        if not driver:
            raise GeoAlgorithmExecutionException(
                u'Could not load GDAL driver for file {}'.format(output))

        crs = map_settings.destinationCrs()

        self.x_tile_count = math.ceil(extent.width() / mupp / tile_size)
        self.y_tile_count = math.ceil(extent.height() / mupp / tile_size)

        xsize = self.x_tile_count * tile_size
        ysize = self.y_tile_count * tile_size

        self.dataset = driver.Create(output, xsize, ysize, 3)  # 3 bands
        self.dataset.SetProjection(str(crs.toWkt()))
        self.dataset.SetGeoTransform(
            [extent.xMinimum(), mupp, 0,
             extent.yMaximum(), 0, -mupp])

        self.image = QImage(QSize(tile_size, tile_size), QImage.Format_RGB32)

        self.settings = QgsMapSettings()
        self.settings.setCrsTransformEnabled(True)
        self.settings.setOutputDpi(self.image.logicalDpiX())
        self.settings.setOutputImageFormat(QImage.Format_RGB32)
        self.settings.setDestinationCrs(crs)
        self.settings.setOutputSize(self.image.size())
        self.settings.setMapUnits(crs.mapUnits())
        self.settings.setFlag(QgsMapSettings.Antialiasing, True)
        self.settings.setFlag(QgsMapSettings.RenderMapTile, True)

        if QgsProject.instance().visibilityPresetCollection().hasPreset(
                map_theme):
            self.settings.setLayers(QgsProject.instance(
            ).visibilityPresetCollection().presetVisibleLayers(map_theme))
            self.settings.setLayerStyleOverrides(QgsProject.instance(
            ).visibilityPresetCollection().presetStyleOverrides(map_theme))
        elif layer:
            self.settings.setLayers([layer])
        else:
            self.settings.setLayers(map_settings.layers())

    def render(self, progress):
        for x in range(self.x_tile_count):
            for y in range(self.y_tile_count):
                cur_tile = x * self.y_tile_count + y
                num_tiles = self.x_tile_count * self.y_tile_count
                progress.setPercentage(cur_tile * 100 / num_tiles)
                self.renderTile(x, y)

    def renderTile(self, x, y):
        """
        Render one tile

        :param x: The x index of the current tile
        :param y: The y index of the current tile
        """
        painter = QPainter(self.image)

        self.settings.setExtent(
            QgsRectangle(
                self.extent.xMinimum() + x * self.mupp * self.tile_size,
                self.extent.yMaximum() - (y + 1) * self.mupp * self.tile_size,
                self.extent.xMinimum() + (x + 1) * self.mupp * self.tile_size,
                self.extent.yMaximum() - y * self.mupp * self.tile_size))

        job = QgsMapRendererCustomPainterJob(self.settings, painter)
        job.renderSynchronously()
        painter.end()

        # Needs not to be deleted or Windows will kill it too early...
        tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
        try:
            self.image.save(tmpfile.name)

            src_ds = osgeo.gdal.Open(tmpfile.name)

            self.dataset.WriteRaster(
                x * self.tile_size, y * self.tile_size, self.tile_size,
                self.tile_size,
                src_ds.ReadRaster(0, 0, self.tile_size, self.tile_size))
        finally:
            del src_ds
            tmpfile.close()
            os.unlink(tmpfile.name)

    def getDriverForFile(self, filename):
        """
        Get the GDAL driver for a filename, based on its extension. (.gpkg, .mbtiles...)
        """
        _, extension = os.path.splitext(filename)

        for i in range(osgeo.gdal.GetDriverCount()):
            driver = osgeo.gdal.GetDriver(i)
            if driver.GetMetadataItem('DMD_EXTENSION') == extension[1:]:
                return driver
Пример #14
0
    def renderer(self):
        qgis = QgsApplication([], False)
        qgis.setPrefixPath(self.settings.get('path'), True)
        qgis.setMaxThreads(1)
        qgis.initQgis()

        while True:
            try:
                fndata, srs, render_size, extended, \
                    target_box, result = self.queue.get()

                layer = QgsVectorLayer(fndata, 'layer', 'ogr')

                crs = QgsCoordinateReferenceSystem(srs.id)
                layer.setCrs(crs)

                settings = QgsMapSettings()
                settings.setLayers([layer.id()])
                settings.setFlag(QgsMapSettings.DrawLabeling)
                settings.setFlag(QgsMapSettings.Antialiasing)

                settings.setCrsTransformEnabled(True)
                settings.setDestinationCrs(crs)
                settings.setMapUnits(crs.mapUnits())
                settings.setOutputSize(QSize(*render_size))
                settings.setExtent(QgsRectangle(*extended))

                settings.setOutputImageFormat(QImage.Format_ARGB32)
                bgcolor = QColor.fromRgba(qRgba(255, 255, 255, 0))
                settings.setBackgroundColor(bgcolor)
                settings.setOutputDpi(96)

                QgsMapLayerRegistry.instance().addMapLayer(layer)
                settings.setLayers([layer.id()])

                # Создаем QImage руками чтобы можно было использовать
                # QgsMapRendererCustomPainterJob. Остальные не позволяют
                # обойти баг с рисованием поверх старого.
                img = QImage(settings.outputSize(), QImage.Format_ARGB32)

                # Эти костыли нужны для того, чтобы корректно рисовались
                # слои на прозрачном фоне, без этого получается каша.
                img.fill(QColor.fromRgba(qRgba(255, 255, 255, 255)))
                img.fill(QColor.fromRgba(qRgba(255, 255, 255, 0)))

                # DPI должно быть таким же как в settings, иначе ошибка. В QImage
                # разрешение указывается в точках на метр по каждой оси.
                dpm = settings.outputDpi() / 25.4 * 1000
                img.setDotsPerMeterX(dpm)
                img.setDotsPerMeterY(dpm)

                painter = QPainter(img)
                job = QgsMapRendererCustomPainterJob(settings, painter)
                job.renderSynchronously()
                painter.end()

                QgsMapLayerRegistry.instance().removeAllMapLayers()

                # Преобразование QImage в PIL
                ba = QByteArray()
                bf = QBuffer(ba)
                bf.open(QIODevice.WriteOnly)
                img.save(bf, 'PNG')
                bf.close()

                buf = StringIO()
                buf.write(bf.data())
                buf.seek(0)

                img = PIL.Image.open(buf)

                # Вырезаем нужный нам кусок изображения
                result.put(img.crop(target_box))

            except Exception as e:
                self.logger.error(e.message)

        qgis.exitQgis()
Пример #15
0
class TestSelectiveMasking(unittest.TestCase):

    def setUp(self):
        self.checker = QgsRenderChecker()
        self.checker.setControlPathPrefix("selective_masking")

        self.report = "<h1>Python Selective Masking Tests</h1>\n"

        self.map_settings = QgsMapSettings()
        crs = QgsCoordinateReferenceSystem('epsg:4326')
        extent = QgsRectangle(-123.0, 22.7, -76.4, 46.9)
        self.map_settings.setBackgroundColor(QColor(152, 219, 249))
        self.map_settings.setOutputSize(QSize(420, 280))
        self.map_settings.setOutputDpi(72)
        self.map_settings.setFlag(QgsMapSettings.Antialiasing, True)
        self.map_settings.setFlag(QgsMapSettings.UseAdvancedEffects, False)
        self.map_settings.setDestinationCrs(crs)
        self.map_settings.setExtent(extent)

        # load a predefined QGIS project
        self.assertTrue(QgsProject.instance().read(os.path.join(unitTestDataPath(), "selective_masking.qgs")))

        self.points_layer = QgsProject.instance().mapLayersByName('points')[0]
        self.lines_layer = QgsProject.instance().mapLayersByName('lines')[0]
        # line layer with subsymbols
        self.lines_layer2 = QgsProject.instance().mapLayersByName('lines2')[0]
        # line layer with labels
        self.lines_with_labels = QgsProject.instance().mapLayersByName('lines_with_labels')[0]

        self.polys_layer = QgsProject.instance().mapLayersByName('polys')[0]
        # polygon layer with a rule based labeling
        self.polys_layer2 = QgsProject.instance().mapLayersByName('polys2')[0]

        self.raster_layer = QgsProject.instance().mapLayersByName('raster_layer')[0]

        # try to fix the font for where labels are defined
        # in order to have more stable image comparison tests
        for layer in [self.polys_layer, self.lines_with_labels, self.polys_layer2]:
            for provider in layer.labeling().subProviders():
                settings = layer.labeling().settings(provider)
                font = getTestFont()
                font.setPointSize(32)
                fmt = settings.format()
                fmt.setFont(font)
                fmt.setNamedStyle('Roman')
                fmt.setSize(32)
                fmt.setSizeUnit(QgsUnitTypes.RenderPoints)
                settings.setFormat(fmt)
                if (layer.geometryType == QgsWkbTypes.PolygonGeometry):
                    settings.placement = QgsPalLayerSettings.OverPoint
                layer.labeling().setSettings(settings, provider)

        # order layers for rendering
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

    def tearDown(self):
        report_file_path = "%s/qgistest.html" % QDir.tempPath()
        with open(report_file_path, 'a') as report_file:
            report_file.write(self.report)

    def check_renderings(self, map_settings, control_name):
        """Test a rendering with different configurations:
        - parallel rendering, no cache
        - sequential rendering, no cache
        - parallel rendering, with cache (rendered two times)
        - sequential rendering, with cache (rendered two times)
        """

        for do_parallel in [False, True]:
            for use_cache in [False, True]:
                print("=== parallel", do_parallel, "cache", use_cache)
                tmp = getTempfilePath('png')
                cache = None
                if use_cache:
                    cache = QgsMapRendererCache()
                    # render a first time to fill the cache
                    renderMapToImageWithTime(self.map_settings, parallel=do_parallel, cache=cache)
                img, t = renderMapToImageWithTime(self.map_settings, parallel=do_parallel, cache=cache)
                img.save(tmp)
                print("Image rendered in {}".format(tmp))

                self.checker.setControlName(control_name)
                self.checker.setRenderedImage(tmp)
                suffix = "_parallel" if do_parallel else "_sequential"
                res = self.checker.compareImages(control_name + suffix)
                self.report += self.checker.report()
                self.assertTrue(res)

                print("=== Rendering took {}s".format(float(t) / 1000.0))

    def test_save_restore_references(self):
        """
        Test saving and restoring symbol layer references
        """

        # simple ids
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some_id", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some_other_id", [4, 5])),
        ])

        # complex ids
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other id, like, this", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other id, like, this", [4, 5])),
        ])

        # complex ids, v2
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("a string; with bits", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("a string; with bits", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
        ])

    def test_label_mask(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask")

    def test_multiple_label_masks_different_sets(self):
        # modify labeling settings of the polys layer
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # modify labeling settings of the lines layer
        label_settings = self.lines_with_labels.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # polygons
            QgsSymbolLayerReference(self.polys_layer.id(), QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        self.lines_with_labels.labeling().setSettings(label_settings)

        # new map settings with a line symbology that has labels
        self.map_settings.setLayers([self.points_layer, self.lines_with_labels, self.polys_layer])
        self.check_renderings(self.map_settings, "multiple_label_masks_different_sets")
        # restore map settings
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

    def test_multiple_label_masks_same_set(self):
        # modify labeling settings of the polys layer
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # modify labeling settings of the lines layer
        label_settings = self.lines_with_labels.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(), QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        self.lines_with_labels.labeling().setSettings(label_settings)

        # new map settings with a line symbology that has labels
        self.map_settings.setLayers([self.points_layer, self.lines_with_labels, self.polys_layer])
        self.check_renderings(self.map_settings, "multiple_label_masks_same_set")
        # restore map settings
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_subsymbol(self):
        # new map settings with a line symbology that has sub symbols
        self.map_settings.setLayers([self.points_layer, self.lines_layer2, self.polys_layer])

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # mask only vertical segments of "roads"
            QgsSymbolLayerReference(self.lines_layer2.id(), QgsSymbolLayerId("", [1, 0])),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask_subsymbol")

        # restore original map settings
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_dd(self):
        """ test label mask with data defined properties """
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        fmt.mask().setEnabled(False)
        fmt.mask().setSize(1.0)
        fmt.mask().setOpacity(0.42)
        # mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        # overwrite with data-defined properties
        fmt.dataDefinedProperties().setProperty(QgsPalLayerSettings.MaskEnabled, QgsProperty.fromExpression('1'))
        fmt.dataDefinedProperties().setProperty(QgsPalLayerSettings.MaskBufferSize, QgsProperty.fromExpression('4.0'))
        fmt.dataDefinedProperties().setProperty(QgsPalLayerSettings.MaskOpacity, QgsProperty.fromExpression('100.0'))

        context = QgsRenderContext()
        fmt.updateDataDefinedProperties(context)

        self.assertEqual(fmt.mask().enabled(), True)
        self.assertEqual(fmt.mask().size(), 4.0)
        self.assertEqual(fmt.mask().opacity(), 1.0)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "label_mask")

    def test_label_mask_rule_labeling(self):
        # new map settings with a rule based labeling
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer2])

        # modify labeling settings of one rule
        for child in self.polys_layer2.labeling().rootRule().children():
            if child.description() == 'Tadam':
                break
        label_settings = child.settings()
        label_settings.priority = 3
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        label_settings.setFormat(fmt)
        child.setSettings(label_settings)

        # modify labeling settings of another rule
        for child in self.polys_layer2.labeling().rootRule().children():
            if child.description() != 'Tadam':
                break
        label_settings = child.settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the polygons
            QgsSymbolLayerReference(self.polys_layer2.id(), QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        child.setSettings(label_settings)

        self.check_renderings(self.map_settings, "rule_label_mask")

        # restore map settings
        self.map_settings.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_symbol_levels(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # enable symbol levels
        self.lines_layer.renderer().setUsingSymbolLevels(True)

        self.check_renderings(self.map_settings, "label_mask_symbol_levels")

    def test_symbol_layer_mask(self):
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        self.check_renderings(self.map_settings, "sl_mask")

    def test_multiple_masks_same_symbol_layer(self):
        """Test multiple masks that occlude the same symbol layer"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "multiple_masks_same_sl")

    def test_multiple_masks_different_symbol_layers_same_layer(self):
        """Test multiple masks that occlude different symbol layers of the same layer.
        The UI should disallow this settings. We test here that only one mask is retained"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "multiple_masks_different_sl")

    def test_multiple_masks_different_symbol_layers_same_layer2(self):
        """Test multiple masks that occlude different symbol layers of the same layer - 2nd possible order
        The UI should disallow this settings. We test here that only one mask is retained"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "multiple_masks_different_sl2")

    def test_mask_symbollayer_preview(self):
        #
        # Masks should be visible in previews
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        p.insertSymbolLayer(0, mask_layer)

        for control_name, render_function in [
                ("as_image", lambda: p.asImage(QSize(64, 64)).save(tmp)),
                ("as_big_preview", lambda: p.bigSymbolPreviewImage().save(tmp)),
                ("sl_preview", lambda:
                 QgsSymbolLayerUtils.symbolLayerPreviewIcon(mask_layer,
                                                            QgsUnitTypes.RenderPixels,
                                                            QSize(64, 64)).pixmap(QSize(64, 64)).save(tmp))
        ]:
            tmp = getTempfilePath('png')
            render_function()
            self.checker.setControlName(control_name)
            self.checker.setRenderedImage(tmp)
            res = self.checker.compareImages(control_name, 90)
            self.report += self.checker.report()
            self.assertTrue(res)

    def test_mask_with_effect(self):
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '12'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 1)),
        ])
        # add an outer glow effect to the mask layer
        blur = QgsOuterGlowEffect.create({"enabled": "1",
                                          "blur_level": "6.445",
                                          "blur_unit": "MM",
                                          "opacity": "1",
                                          "spread": "0.6",
                                          "spread_unit": "MM",
                                          "color1": "0,0,255,255",
                                          "draw_mode": "2"
                                          })
        mask_layer.setPaintEffect(blur)
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        self.check_renderings(self.map_settings, "mask_with_effect")

    def test_label_mask_with_effect(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        # add an outer glow effect to the mask
        blur = QgsOuterGlowEffect.create({"enabled": "1",
                                          "blur_level": "6.445",
                                          "blur_unit": "MM",
                                          "opacity": "1",
                                          "spread": "0.6",
                                          "spread_unit": "MM",
                                          "color1": "0,0,255,255",
                                          "draw_mode": "2"
                                          })
        fmt.mask().setPaintEffect(blur)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask_with_effect")

    def test_layout_exports(self):
        """Test mask effects in a layout export at 300 dpi"""
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(), QgsSymbolLayerId("Jet", 0))])

        # add an outer glow effect to the mask
        blur = QgsOuterGlowEffect.create({"enabled": "1",
                                          "blur_level": "6.445",
                                          "blur_unit": "MM",
                                          "opacity": "1",
                                          "spread": "0.6",
                                          "spread_unit": "MM",
                                          "color1": "0,0,255,255",
                                          "draw_mode": "2"
                                          })
        fmt.mask().setPaintEffect(blur)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        layout = QgsLayout(QgsProject.instance())
        page = QgsLayoutItemPage(layout)
        page.setPageSize(QgsLayoutSize(50, 33))
        layout.pageCollection().addPage(page)

        map = QgsLayoutItemMap(layout)
        map.attemptSetSceneRect(QRectF(1, 1, 48, 32))
        map.setFrameEnabled(True)
        layout.addLayoutItem(map)
        map.setExtent(self.lines_layer.extent())
        map.setLayers([self.points_layer, self.lines_layer, self.polys_layer])

        image = QImage(591, 591, QImage.Format_RGB32)
        image.setDotsPerMeterX(int(300 / 25.3 * 1000))
        image.setDotsPerMeterY(int(300 / 25.3 * 1000))
        image.fill(0)
        p = QPainter(image)
        exporter = QgsLayoutExporter(layout)
        exporter.renderPage(p, 0)
        p.end()

        tmp = getTempfilePath('png')
        image.save(tmp)

        control_name = "layout_export"
        self.checker.setControlName(control_name)
        self.checker.setRenderedImage(tmp)
        res = self.checker.compareImages(control_name)
        self.report += self.checker.report()
        self.assertTrue(res)

    def test_different_dpi_target(self):
        """Test with raster layer and a target dpi"""

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(), QgsSymbolLayerId("", 0))])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.map_settings.setLayers([self.lines_layer, self.polys_layer, self.raster_layer])
        self.map_settings.setDpiTarget(300)
        self.check_renderings(self.map_settings, "different_dpi_target")
Пример #16
0
class TestSelectiveMasking(unittest.TestCase):
    def setUp(self):
        self.checker = QgsRenderChecker()
        self.checker.setControlPathPrefix("selective_masking")

        self.report = "<h1>Python Selective Masking Tests</h1>\n"

        self.map_settings = QgsMapSettings()
        crs = QgsCoordinateReferenceSystem('epsg:4326')
        extent = QgsRectangle(-123.0, 22.7, -76.4, 46.9)
        self.map_settings.setBackgroundColor(QColor(152, 219, 249))
        self.map_settings.setOutputSize(QSize(420, 280))
        self.map_settings.setOutputDpi(72)
        self.map_settings.setFlag(QgsMapSettings.Antialiasing, True)
        self.map_settings.setFlag(QgsMapSettings.UseAdvancedEffects, False)
        self.map_settings.setDestinationCrs(crs)
        self.map_settings.setExtent(extent)

        # load a predefined QGIS project
        self.assertTrue(QgsProject.instance().read(
            os.path.join(unitTestDataPath(), "selective_masking.qgs")))

        self.points_layer = QgsProject.instance().mapLayersByName('points')[0]
        self.lines_layer = QgsProject.instance().mapLayersByName('lines')[0]
        # line layer with subsymbols
        self.lines_layer2 = QgsProject.instance().mapLayersByName('lines2')[0]
        # line layer with labels
        self.lines_with_labels = QgsProject.instance().mapLayersByName(
            'lines_with_labels')[0]

        self.polys_layer = QgsProject.instance().mapLayersByName('polys')[0]
        # polygon layer with a rule based labeling
        self.polys_layer2 = QgsProject.instance().mapLayersByName('polys2')[0]

        self.raster_layer = QgsProject.instance().mapLayersByName(
            'raster_layer')[0]

        # try to fix the font for where labels are defined
        # in order to have more stable image comparison tests
        for layer in [
                self.polys_layer, self.lines_with_labels, self.polys_layer2
        ]:
            for provider in layer.labeling().subProviders():
                settings = layer.labeling().settings(provider)
                font = getTestFont()
                font.setPointSize(32)
                fmt = settings.format()
                fmt.setFont(font)
                fmt.setNamedStyle('Roman')
                fmt.setSize(32)
                fmt.setSizeUnit(QgsUnitTypes.RenderPoints)
                settings.setFormat(fmt)
                if (layer.geometryType == QgsWkbTypes.PolygonGeometry):
                    settings.placement = QgsPalLayerSettings.OverPoint
                layer.labeling().setSettings(settings, provider)

        # order layers for rendering
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer])

    def tearDown(self):
        report_file_path = "%s/qgistest.html" % QDir.tempPath()
        with open(report_file_path, 'a') as report_file:
            report_file.write(self.report)

    def check_renderings(self, map_settings, control_name):
        """Test a rendering with different configurations:
        - parallel rendering, no cache
        - sequential rendering, no cache
        - parallel rendering, with cache (rendered two times)
        - sequential rendering, with cache (rendered two times)
        """

        for do_parallel in [False, True]:
            for use_cache in [False, True]:
                print("=== parallel", do_parallel, "cache", use_cache)
                tmp = getTempfilePath('png')
                cache = None
                if use_cache:
                    cache = QgsMapRendererCache()
                    # render a first time to fill the cache
                    renderMapToImageWithTime(self.map_settings,
                                             parallel=do_parallel,
                                             cache=cache)
                img, t = renderMapToImageWithTime(self.map_settings,
                                                  parallel=do_parallel,
                                                  cache=cache)
                img.save(tmp)
                print("Image rendered in {}".format(tmp))

                self.checker.setControlName(control_name)
                self.checker.setRenderedImage(tmp)
                suffix = ("_parallel" if do_parallel else "_sequential") + (
                    "_cache" if use_cache else "_nocache")
                res = self.checker.compareImages(control_name + suffix)
                self.report += self.checker.report()
                self.assertTrue(res)

                print("=== Rendering took {}s".format(float(t) / 1000.0))

    def check_layout_export(self,
                            control_name,
                            expected_nb_raster,
                            layers=None,
                            dpiTarget=None):
        """
        Generate a PDF layout export and control the output matches expected_filename
        """

        # generate vector file
        layout = QgsLayout(QgsProject.instance())
        page = QgsLayoutItemPage(layout)
        page.setPageSize(QgsLayoutSize(50, 33))
        layout.pageCollection().addPage(page)

        map = QgsLayoutItemMap(layout)
        map.attemptSetSceneRect(QRectF(1, 1, 48, 32))
        map.setFrameEnabled(True)
        layout.addLayoutItem(map)
        map.setExtent(self.lines_layer.extent())
        map.setLayers(layers if layers is not None else
                      [self.points_layer, self.lines_layer, self.polys_layer])

        settings = QgsLayoutExporter.PdfExportSettings()

        if dpiTarget is not None:
            settings.dpi = dpiTarget

        exporter = QgsLayoutExporter(layout)
        result_filename = getTempfilePath('pdf')
        exporter.exportToPdf(result_filename, settings)
        self.assertTrue(os.path.exists(result_filename))

        # Generate a readable PDF file so we count raster in it
        result_txt = getTempfilePath("txt")
        subprocess.run([
            "qpdf", "--qdf", "--object-streams=disable", result_filename,
            result_txt
        ])
        self.assertTrue(os.path.exists(result_txt))

        # expected_file = os.path.join(TEST_DATA_DIR, "control_images/selective_masking/pdf_exports/{}".format(expected_filename))
        # self.assertTrue(os.path.exists(expected_file))

        result = open(result_txt, 'rb')
        result_lines = [l.decode('iso-8859-1') for l in result.readlines()]
        result.close()
        nb_raster = len([l for l in result_lines if "/Subtype /Image" in l])
        self.assertEqual(nb_raster, expected_nb_raster)

        # Generate an image from pdf to compare with expected control image
        # keep PDF DPI resolution (300)
        image_result_filename = getTempfilePath("png")
        subprocess.run([
            "pdftoppm", result_filename,
            os.path.splitext(image_result_filename)[0], "-png", "-r", "300",
            "-singlefile"
        ])

        self.checker.setControlName(control_name)
        self.checker.setRenderedImage(image_result_filename)
        res = self.checker.compareImages(control_name)
        self.report += self.checker.report()
        self.assertTrue(res)

    def test_save_restore_references(self):
        """
        Test saving and restoring symbol layer references
        """

        # simple ids
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(),
                                    QgsSymbolLayerId("some_id",
                                                     [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(),
                                    QgsSymbolLayerId("some_other_id", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(self.lines_layer2.id(),
                                    QgsSymbolLayerId("some_id",
                                                     [1, 3, 5, 19])),
            QgsSymbolLayerReference(self.polys_layer.id(),
                                    QgsSymbolLayerId("some_other_id", [4, 5])),
        ])

        # complex ids
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(
                self.lines_layer2.id(),
                QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(
                self.polys_layer.id(),
                QgsSymbolLayerId("some other id, like, this", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            QgsSymbolLayerReference(
                self.lines_layer2.id(),
                QgsSymbolLayerId("some id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(
                self.polys_layer.id(),
                QgsSymbolLayerId("some other id, like, this", [4, 5])),
        ])

        # complex ids, v2
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setMasks([
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("a string; with bits",
                                                     0)),
            QgsSymbolLayerReference(
                self.lines_layer2.id(),
                QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(
                self.polys_layer.id(),
                QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
        ])

        props = mask_layer.properties()

        mask_layer2 = QgsMaskMarkerSymbolLayer.create(props)
        self.assertEqual(mask_layer2.masks(), [
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("a string; with bits",
                                                     0)),
            QgsSymbolLayerReference(
                self.lines_layer2.id(),
                QgsSymbolLayerId("some; id, #1", [1, 3, 5, 19])),
            QgsSymbolLayerReference(
                self.polys_layer.id(),
                QgsSymbolLayerId("some other; id, lik;e, this", [4, 5])),
        ])

    def test_label_mask(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask")

    def test_multiple_label_masks_different_sets(self):
        # modify labeling settings of the polys layer
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # modify labeling settings of the lines layer
        label_settings = self.lines_with_labels.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # polygons
            QgsSymbolLayerReference(self.polys_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        self.lines_with_labels.labeling().setSettings(label_settings)

        # new map settings with a line symbology that has labels
        self.map_settings.setLayers(
            [self.points_layer, self.lines_with_labels, self.polys_layer])
        self.check_renderings(self.map_settings,
                              "multiple_label_masks_different_sets")
        # restore map settings
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer])

    def test_multiple_label_masks_same_set(self):
        # modify labeling settings of the polys layer
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(),
                                    QgsSymbolLayerId("", 0)),
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # modify labeling settings of the lines layer
        label_settings = self.lines_with_labels.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_with_labels.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        self.lines_with_labels.labeling().setSettings(label_settings)

        # new map settings with a line symbology that has labels
        self.map_settings.setLayers(
            [self.points_layer, self.lines_with_labels, self.polys_layer])
        self.check_renderings(self.map_settings,
                              "multiple_label_masks_same_set")
        # restore map settings
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_subsymbol(self):
        # new map settings with a line symbology that has sub symbols
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer2, self.polys_layer])

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # mask only vertical segments of "roads"
            QgsSymbolLayerReference(self.lines_layer2.id(),
                                    QgsSymbolLayerId("", [1, 0])),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask_subsymbol")

        # restore original map settings
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_dd(self):
        """ test label mask with data defined properties """
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        fmt.mask().setEnabled(False)
        fmt.mask().setSize(1.0)
        fmt.mask().setOpacity(0.42)
        # mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        # overwrite with data-defined properties
        fmt.dataDefinedProperties().setProperty(
            QgsPalLayerSettings.MaskEnabled, QgsProperty.fromExpression('1'))
        fmt.dataDefinedProperties().setProperty(
            QgsPalLayerSettings.MaskBufferSize,
            QgsProperty.fromExpression('4.0'))
        fmt.dataDefinedProperties().setProperty(
            QgsPalLayerSettings.MaskOpacity,
            QgsProperty.fromExpression('100.0'))

        context = QgsRenderContext()
        fmt.updateDataDefinedProperties(context)

        self.assertEqual(fmt.mask().enabled(), True)
        self.assertEqual(fmt.mask().size(), 4.0)
        self.assertEqual(fmt.mask().opacity(), 1.0)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "label_mask")

    def test_label_mask_rule_labeling(self):
        # new map settings with a rule based labeling
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer2])

        # modify labeling settings of one rule
        for child in self.polys_layer2.labeling().rootRule().children():
            if child.description() == 'Tadam':
                break
        label_settings = child.settings()
        label_settings.priority = 3
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        child.setSettings(label_settings)

        # modify labeling settings of another rule
        for child in self.polys_layer2.labeling().rootRule().children():
            if child.description() != 'Tadam':
                break
        label_settings = child.settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the polygons
            QgsSymbolLayerReference(self.polys_layer2.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        label_settings.setFormat(fmt)
        child.setSettings(label_settings)

        self.check_renderings(self.map_settings, "rule_label_mask")

        # restore map settings
        self.map_settings.setLayers(
            [self.points_layer, self.lines_layer, self.polys_layer])

    def test_label_mask_symbol_levels(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        # enable symbol levels
        self.lines_layer.renderer().setUsingSymbolLevels(True)

        self.check_renderings(self.map_settings, "label_mask_symbol_levels")

    def test_symbol_layer_mask(self):
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        self.check_renderings(self.map_settings, "sl_mask")

    def test_multiple_masks_same_symbol_layer(self):
        """Test multiple masks that occlude the same symbol layer"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "multiple_masks_same_sl")

    def test_multiple_masks_different_symbol_layers_same_layer(self):
        """Test multiple masks that occlude different symbol layers of the same layer.
        The UI should disallow this settings. We test here that only one mask is retained"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 1)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings, "multiple_masks_different_sl")

    def test_multiple_masks_different_symbol_layers_same_layer2(self):
        """Test multiple masks that occlude different symbol layers of the same layer - 2nd possible order
        The UI should disallow this settings. We test here that only one mask is retained"""
        #
        # 1. a symbol layer mask
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        #
        # 2. a label mask
        #

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 1))
        ])
        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_renderings(self.map_settings,
                              "multiple_masks_different_sl2")

    def test_mask_symbollayer_preview(self):
        #
        # Masks should be visible in previews
        #
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '10'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        p.insertSymbolLayer(0, mask_layer)

        for control_name, render_function in [
            ("as_image", lambda: p.asImage(QSize(64, 64)).save(tmp)),
            ("as_big_preview", lambda: p.bigSymbolPreviewImage().save(tmp)),
            ("sl_preview", lambda: QgsSymbolLayerUtils.symbolLayerPreviewIcon(
                mask_layer, QgsUnitTypes.RenderPixels, QSize(64, 64)).pixmap(
                    QSize(64, 64)).save(tmp))
        ]:
            tmp = getTempfilePath('png')
            render_function()
            self.checker.setControlName(control_name)
            self.checker.setRenderedImage(tmp)
            res = self.checker.compareImages(control_name, 90)
            self.report += self.checker.report()
            self.assertTrue(res)

    def test_mask_with_effect(self):
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "7"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '12'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the yellow part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 1)),
        ])
        # add an outer glow effect to the mask layer
        blur = QgsOuterGlowEffect.create({
            "enabled": "1",
            "blur_level": "6.445",
            "blur_unit": "MM",
            "opacity": "1",
            "spread": "0.6",
            "spread_unit": "MM",
            "color1": "0,0,255,255",
            "draw_mode": "2"
        })
        mask_layer.setPaintEffect(blur)
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        self.check_renderings(self.map_settings, "mask_with_effect")

    def test_label_mask_with_effect(self):
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        # add an outer glow effect to the mask
        blur = QgsOuterGlowEffect.create({
            "enabled": "1",
            "blur_level": "6.445",
            "blur_unit": "MM",
            "opacity": "1",
            "spread": "0.6",
            "spread_unit": "MM",
            "color1": "0,0,255,255",
            "draw_mode": "2"
        })
        fmt.mask().setPaintEffect(blur)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        format = self.polys_layer.labeling().settings().format()
        self.assertTrue(format.mask().enabled())

        self.check_renderings(self.map_settings, "label_mask_with_effect")

        # test that force vector output has no impact on the result
        self.map_settings.setFlag(Qgis.MapSettingsFlag.ForceVectorOutput, True)
        self.check_renderings(self.map_settings, "label_mask_with_effect")

    def test_different_dpi_target(self):
        """Test with raster layer and a target dpi"""

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.map_settings.setLayers(
            [self.lines_layer, self.polys_layer, self.raster_layer])
        self.map_settings.setDpiTarget(300)
        self.check_renderings(self.map_settings, "different_dpi_target")

        # test with high dpi screen
        self.map_settings.setDevicePixelRatio(2)
        self.check_renderings(self.map_settings, "different_dpi_target_hdpi")

    def test_layout_export(self):
        """Test mask effects in a layout export at 300 dpi"""
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask

        fmt.font().setPointSize(4)

        fmt.mask().setEnabled(True)
        fmt.mask().setSize(1.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_layout_export("layout_export", 0)

    def test_layout_export_w_effects(self):
        """Test mask effects in a layout export at 300 dpi"""
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask

        fmt.font().setPointSize(4)

        fmt.mask().setEnabled(True)
        fmt.mask().setSize(1.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        # add an outer glow effect to the mask
        blur = QgsOuterGlowEffect.create({
            "enabled": "1",
            "blur_level": "3.445",
            "blur_unit": "MM",
            "opacity": "1",
            "spread": "0.06",
            "spread_unit": "MM",
            "color1": "0,0,255,255",
            "draw_mode": "2"
        })
        fmt.mask().setPaintEffect(blur)

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        # 4 rasters : Image and its mask for masked point and lines layer
        self.check_layout_export("layout_export_w_effects", 4)

    def test_layout_export_marker_masking(self):
        """Test mask effects in a layout export with a marker symbol masking"""

        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "3"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '6'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        self.check_layout_export("layout_export_marker_masking", 0)

    def test_layout_export_marker_masking_w_effects(self):
        """Test mask effects in a layout export with a marker symbol masking"""

        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "3"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '6'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])

        # add an outer glow effect to the mask
        blur = QgsOuterGlowEffect.create({
            "enabled": "1",
            "blur_level": "3.445",
            "blur_unit": "MM",
            "opacity": "1",
            "spread": "0.06",
            "spread_unit": "MM",
            "color1": "0,0,255,255",
            "draw_mode": "2"
        })

        # TODO try to set the mask effect on p the marker symbol -> result should be the same
        mask_layer.setPaintEffect(blur)

        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        # 2 rasters : Image and its mask for masked lines layer
        self.check_layout_export("layout_export_marker_masking_w_effects", 2)

    def test_layout_export_w_raster(self):
        """Test layout export with raster beneath the masked area"""

        # just decrease the yellow line so we see the raster on masked area
        self.lines_layer.renderer().symbol().symbolLayers()[1].setWidth(0.5)

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask

        fmt.font().setPointSize(4)

        fmt.mask().setEnabled(True)
        fmt.mask().setSize(1.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        # 1 raster : the raster layer
        self.check_layout_export(
            "layout_export_w_raster", 1,
            [self.lines_layer, self.polys_layer, self.raster_layer])

    def test_layout_export_w_force_raster_render(self):
        """
        Test layout export with a marker symbol masking forced to be render as raster
        We expect the lines to be masked and the whole output needs to be vector except
        the marker layer forced as raster
        """

        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "3"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '6'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)
        self.points_layer.renderer().setForceRasterRender(True)

        # 2 rasters : Image and its mask for the points layer
        self.check_layout_export("layout_export_force_raster_render", 2,
                                 [self.points_layer, self.lines_layer])

    def test_layout_export_marker_masking_w_transparency(self):
        """Test layout export with a marker symbol masking which has an opacity lower than 1"""

        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "3"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '6'})
        circle_symbol.setOpacity(0.5)
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        # add this mask layer to the point layer
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        # 2 rasters (mask + image) because opacity force rasterization of the masked line layers
        self.check_layout_export("layout_export_marker_masking_w_transparency",
                                 2)

    def test_layout_export_text_masking_w_transparency(self):
        """Test mask effects in a layout export at 300 dpi"""
        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask

        fmt.font().setPointSize(4)

        fmt.mask().setEnabled(True)
        fmt.mask().setSize(1.0)
        fmt.mask().setOpacity(0.5)

        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
            # the black jets
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("B52", 0)),
            QgsSymbolLayerReference(self.points_layer.id(),
                                    QgsSymbolLayerId("Jet", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        # 4 rasters (mask+image per masked layer) because opacity force rasterization
        # of the masked line and point layers
        self.check_layout_export("layout_export_text_masking_w_transparency",
                                 4)

    def test_different_dpi_target_vector(self):
        """Test rendering a raster layer with vector output and a target dpi
        Used when layout previewing
        """

        # modify labeling settings
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()
        # enable a mask
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(4.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.map_settings.setLayers(
            [self.lines_layer, self.polys_layer, self.raster_layer])
        self.map_settings.setOutputDpi(81)
        self.map_settings.setDpiTarget(300)
        self.map_settings.setFlag(Qgis.MapSettingsFlag.ForceVectorOutput, True)

        image = QImage(self.map_settings.deviceOutputSize(),
                       self.map_settings.outputImageFormat())
        image.setDevicePixelRatio(self.map_settings.devicePixelRatio())
        image.setDotsPerMeterX(int(1000 * self.map_settings.outputDpi() /
                                   25.4))
        image.setDotsPerMeterY(int(1000 * self.map_settings.outputDpi() /
                                   25.4))
        image.fill(Qt.transparent)
        pImg = QPainter()
        pImg.begin(image)
        job = QgsMapRendererCustomPainterJob(self.map_settings, pImg)
        job.start()
        job.waitForFinished()
        pImg.end()
        tmp = getTempfilePath('png')
        image.save(tmp)

        control_name = "different_dpi_target_vector"
        self.checker.setControlName(control_name)
        self.checker.setRenderedImage(tmp)
        res = self.checker.compareImages(control_name)
        self.report += self.checker.report()
        self.assertTrue(res)

        # Same test with high dpi
        self.map_settings.setDevicePixelRatio(2)
        image = QImage(self.map_settings.deviceOutputSize(),
                       self.map_settings.outputImageFormat())
        image.setDevicePixelRatio(self.map_settings.devicePixelRatio())
        image.setDotsPerMeterX(int(1000 * self.map_settings.outputDpi() /
                                   25.4))
        image.setDotsPerMeterY(int(1000 * self.map_settings.outputDpi() /
                                   25.4))
        image.fill(Qt.transparent)
        pImg = QPainter()
        pImg.begin(image)
        job = QgsMapRendererCustomPainterJob(self.map_settings, pImg)
        job.start()
        job.waitForFinished()
        pImg.end()
        tmp = getTempfilePath('png')
        image.save(tmp)

        control_name = "different_dpi_target_vector_hdpi"
        self.checker.setControlName(control_name)
        self.checker.setRenderedImage(tmp)
        res = self.checker.compareImages(control_name)
        self.report += self.checker.report()
        self.assertTrue(res)

    def test_layout_export_2_sources_masking(self):
        """Test masking with 2 different sources"""

        # mask with points layer circles...
        p = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'size': "3"})
        self.points_layer.setRenderer(QgsSingleSymbolRenderer(p))

        circle_symbol = QgsMarkerSymbol.createSimple({'size': '6'})
        mask_layer = QgsMaskMarkerSymbolLayer()
        mask_layer.setSubSymbol(circle_symbol)
        mask_layer.setMasks([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0)),
        ])
        self.points_layer.renderer().symbol().appendSymbolLayer(mask_layer)

        # ...and with text
        label_settings = self.polys_layer.labeling().settings()
        fmt = label_settings.format()

        fmt.font().setPointSize(4)
        fmt.mask().setEnabled(True)
        fmt.mask().setSize(1.0)
        # and mask other symbol layers underneath
        fmt.mask().setMaskedSymbolLayers([
            # the black part of roads
            QgsSymbolLayerReference(self.lines_layer.id(),
                                    QgsSymbolLayerId("", 0))
        ])

        label_settings.setFormat(fmt)
        self.polys_layer.labeling().setSettings(label_settings)

        self.check_layout_export("layout_export_2_sources_masking", 0)
Пример #17
0
class TileSet():
    """
    A set of tiles
    """
    def __init__(self, map_theme, layer, extent, tile_size, mupp, output,
                 make_trans, map_settings, project):
        """
        :param map_theme:
        :param extent:
        :param layer:
        :param tile_size:
        :param mupp:
        :param output:
        :param map_settings: Map canvas map settings used for some fallback
        values and CRS
        """

        self.extent = extent
        self.mupp = mupp
        self.tile_size = tile_size

        driver = self.getDriverForFile(output)

        if not driver:
            raise QgsProcessingException(
                u'Could not load GDAL driver for file {}'.format(output))

        crs = map_settings.destinationCrs()

        self.x_tile_count = math.ceil(extent.width() / mupp / tile_size)
        self.y_tile_count = math.ceil(extent.height() / mupp / tile_size)

        xsize = self.x_tile_count * tile_size
        ysize = self.y_tile_count * tile_size

        if make_trans:
            no_bands = 4
        else:
            no_bands = 3

        self.dataset = driver.Create(output, xsize, ysize, no_bands)
        self.dataset.SetProjection(str(crs.toWkt()))
        self.dataset.SetGeoTransform(
            [extent.xMinimum(), mupp, 0,
             extent.yMaximum(), 0, -mupp])

        self.image = QImage(QSize(tile_size, tile_size), QImage.Format_ARGB32)

        self.settings = QgsMapSettings()
        self.settings.setOutputDpi(self.image.logicalDpiX())
        self.settings.setOutputImageFormat(QImage.Format_ARGB32)
        self.settings.setDestinationCrs(crs)
        self.settings.setOutputSize(self.image.size())
        self.settings.setFlag(QgsMapSettings.Antialiasing, True)
        self.settings.setFlag(QgsMapSettings.RenderMapTile, True)
        self.settings.setFlag(QgsMapSettings.UseAdvancedEffects, True)

        r = project.readNumEntry('Gui', '/CanvasColorRedPart', 255)[0]
        g = project.readNumEntry('Gui', '/CanvasColorGreenPart', 255)[0]
        b = project.readNumEntry('Gui', '/CanvasColorBluePart', 255)[0]
        if make_trans:
            self.bgColor = QColor(r, g, b, 0)
        else:
            self.bgColor = QColor(r, g, b)
        self.settings.setBackgroundColor(self.bgColor)

        if QgsProject.instance().mapThemeCollection().hasMapTheme(map_theme):
            self.settings.setLayers(QgsProject.instance().mapThemeCollection().
                                    mapThemeVisibleLayers(map_theme))
            self.settings.setLayerStyleOverrides(QgsProject.instance(
            ).mapThemeCollection().mapThemeStyleOverrides(map_theme))
        elif layer:
            self.settings.setLayers([layer])
        else:
            self.settings.setLayers(map_settings.layers())

    def render(self, feedback, make_trans):
        for x in range(self.x_tile_count):
            for y in range(self.y_tile_count):
                if feedback.isCanceled():
                    return
                cur_tile = x * self.y_tile_count + y
                num_tiles = self.x_tile_count * self.y_tile_count
                self.renderTile(x, y, feedback, make_trans)

                feedback.setProgress(int((cur_tile / num_tiles) * 100))

    def renderTile(self, x, y, feedback, make_trans):
        """
        Render one tile
        :param x: The x index of the current tile
        :param y: The y index of the current tile
        """

        if make_trans:
            self.image.fill(self.bgColor.rgba())
        else:
            self.image.fill(self.bgColor.rgb())

        painter = QPainter(self.image)

        self.settings.setExtent(
            QgsRectangle(
                self.extent.xMinimum() + x * self.mupp * self.tile_size,
                self.extent.yMaximum() - (y + 1) * self.mupp * self.tile_size,
                self.extent.xMinimum() + (x + 1) * self.mupp * self.tile_size,
                self.extent.yMaximum() - y * self.mupp * self.tile_size))

        job = QgsMapRendererCustomPainterJob(self.settings, painter)
        job.renderSynchronously()
        painter.end()

        # Needs not to be deleted or Windows will kill it too early...
        tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
        try:
            self.image.save(tmpfile.name)

            src_ds = osgeo.gdal.Open(tmpfile.name)

            self.dataset.WriteRaster(
                x * self.tile_size, y * self.tile_size, self.tile_size,
                self.tile_size,
                src_ds.ReadRaster(0, 0, self.tile_size, self.tile_size))
        except Exception as e:
            feedback.reportError(str(e))
        finally:
            del src_ds
            tmpfile.close()
            os.unlink(tmpfile.name)

    def getDriverForFile(self, filename):
        """
        Get the GDAL driver for a filename, based on its extension. (.gpkg,
        .mbtiles...)
        """
        _, extension = os.path.splitext(filename)

        # If no extension is set, use .tif as default
        if extension == '':
            extension = '.tif'

        driver_name = QgsRasterFileWriter.driverForExtension(extension[1:])
        return osgeo.gdal.GetDriverByName(driver_name)
Пример #18
0
    print('crs authid: %s' % layer.crs().authid())
    print('extent: %s' % layer.sourceExtent().toString())
    QgsProject.instance().addMapLayer(layer)

    image = QImage(QSize(600, 400), QImage.Format_ARGB32_Premultiplied)

    image.fill(QColor(255, 255, 128).rgb())

    painter = QPainter(image)

    settings = QgsMapSettings()
    settings.setOutputDpi(image.logicalDpiX())
    settings.setOutputImageFormat(QImage.Format_ARGB32)
    settings.setDestinationCrs(layer.crs())
    settings.setOutputSize(image.size())
    settings.setFlag(QgsMapSettings.Antialiasing, True)
    settings.setFlag(QgsMapSettings.RenderMapTile, True)
    settings.setFlag(QgsMapSettings.UseAdvancedEffects, True)
    settings.setBackgroundColor(QColor(255, 255, 255, 0))

    settings.setLayers([layer])
    settings.setExtent(layer.extent())

    job = QgsMapRendererCustomPainterJob(settings, painter)
    job.renderSynchronously()
    painter.end()

    output_path = os.environ.get('QGIS_DJANGO_PROVIDER_TEST_OUTPUT',
                                 '/tmp/qgis-django-provider-test.png')
    image.save(output_path, 'png')