Example #1
0
    def testOperators(self):
        rect1 = QgsRectangle(10, 20, 40, 40)
        rect2 = rect1 + QgsVector(3, 5.5)
        assert rect2 == QgsRectangle(13, 25.5, 43, 45.5), "QgsRectangle + operator does no work"

        # Subtracting the center point, so it becomes zero.
        rect1 -= rect1.center() - QgsPointXY(0, 0)
        assert rect1.center() == QgsPointXY(0, 0)
Example #2
0
    def testOperators(self):
        rect1 = QgsRectangle(10, 20, 40, 40)
        rect2 = rect1 + QgsVector(3, 5.5)
        self.assertEqual(rect2, QgsRectangle(13, 25.5, 43, 45.5))

        # Subtracting the center point, so it becomes zero.
        rect1 -= rect1.center() - QgsPointXY(0, 0)
        self.assertEqual(rect1.center(), QgsPointXY(0, 0))
Example #3
0
    def testOperators(self):
        rect1 = QgsRectangle(10, 20, 40, 40)
        rect2 = rect1 + QgsVector(3, 5.5)
        assert rect2 == QgsRectangle(13, 25.5, 43, 45.5), "QgsRectangle + operator does no work"

        # Subtracting the center point, so it becomes zero.
        rect1 -= rect1.center() - QgsPointXY(0, 0)
        assert rect1.center() == QgsPointXY(0, 0)
Example #4
0
    def processAlgorithm(self, progress):
        idx = self.getParameterValue(self.TYPE)
        extent = self.getParameterValue(self.EXTENT).split(',')
        hSpacing = self.getParameterValue(self.HSPACING)
        vSpacing = self.getParameterValue(self.VSPACING)
        crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS))

        bbox = QgsRectangle(float(extent[0]), float(extent[2]),
                            float(extent[1]), float(extent[3]))

        width = bbox.width()
        height = bbox.height()
        centerX = bbox.center().x()
        centerY = bbox.center().y()
        #~ originX = centerX - width / 2.0
        #~ originY = centerY - height / 2.0
        originX = bbox.xMinimum()
        originY = bbox.yMaximum()

        if hSpacing <= 0 or vSpacing <= 0:
            raise GeoAlgorithmExecutionException(
                self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing)))

        if width < hSpacing:
            raise GeoAlgorithmExecutionException(
                self.tr('Horizontal spacing is too small for the covered area'))

        if height < vSpacing:
            raise GeoAlgorithmExecutionException(
                self.tr('Vertical spacing is too small for the covered area'))

        #if self.types[idx].find(self.tr('polygon')) >= 0:
        if idx != 0:
            geometryType = QGis.WKBPolygon
        else:
            geometryType = QGis.WKBLineString

        fields = [QgsField('left', QVariant.Double, '', 24, 16),
                  QgsField('top', QVariant.Double, '', 24, 16),
                  QgsField('right', QVariant.Double, '', 24, 16),
                  QgsField('bottom', QVariant.Double, '', 24, 16)
                  ]

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields,
                                                                     geometryType, crs)

        if idx == 0:
            self._rectangleGridLine(
                writer, width, height, originX, originY, hSpacing, vSpacing)
        elif idx == 1:
            self._rectangleGridPoly(
                writer, width, height, originX, originY, hSpacing, vSpacing)
        elif idx == 2:
            self._diamondGrid(
                writer, width, height, originX, originY, hSpacing, vSpacing)
        elif idx == 3:
            self._hexagonGrid(
                writer, width, height, originX, originY, hSpacing, vSpacing)

        del writer
    def update_extent(self, extent):
        """Update extent value in GUI based from an extent.

        :param extent: A list in the form [xmin, ymin, xmax, ymax] where all
            coordinates provided are in Geographic / EPSG:4326.
        :type extent: list
        """
        self.x_minimum.setValue(extent[0])
        self.y_minimum.setValue(extent[1])
        self.x_maximum.setValue(extent[2])
        self.y_maximum.setValue(extent[3])

        # Updating the country if possible.
        rectangle = QgsRectangle(extent[0], extent[1], extent[2], extent[3])
        center = rectangle.center()

        for country in self.bbox_countries:
            for polygon in self.bbox_countries[country]:
                if polygon.contains(center):
                    index = self.country_comboBox.findText(country)
                    self.country_comboBox.setCurrentIndex(index)
                    break
            else:
                # Continue if the inner loop wasn't broken.
                continue
            # Inner loop was broken, break the outer.
            break
        else:
            self.country_comboBox.setCurrentIndex(0)
    def update_extent(self, extent):
        """Update extent value in GUI based from an extent.

        :param extent: A list in the form [xmin, ymin, xmax, ymax] where all
            coordinates provided are in Geographic / EPSG:4326.
        :type extent: list
        """
        self.x_minimum.setValue(extent[0])
        self.y_minimum.setValue(extent[1])
        self.x_maximum.setValue(extent[2])
        self.y_maximum.setValue(extent[3])

        # Updating the country if possible.
        rectangle = QgsRectangle(extent[0], extent[1], extent[2], extent[3])
        center = rectangle.center()

        for country in self.bbox_countries:
            for polygon in self.bbox_countries[country]:
                if polygon.contains(center):
                    index = self.country_comboBox.findText(country)
                    self.country_comboBox.setCurrentIndex(index)
                    break
            else:
                # Continue if the inner loop wasn't broken.
                continue
            # Inner loop was broken, break the outer.
            break
        else:
            self.country_comboBox.setCurrentIndex(0)
Example #7
0
    def processAlgorithm(self, progress):
        idx = self.getParameterValue(self.TYPE)
        extent = self.getParameterValue(self.EXTENT).split(',')
        hSpacing = self.getParameterValue(self.HSPACING)
        vSpacing = self.getParameterValue(self.VSPACING)

        bbox = QgsRectangle(float(extent[0]), float(extent[2]),
                            float(extent[1]), float(extent[3]))

        width = bbox.width()
        height = bbox.height()
        centerX = bbox.center().x()
        centerY = bbox.center().y()
        originX = centerX - width / 2.0
        originY = centerY - height / 2.0
        crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS))

        if hSpacing <= 0 or vSpacing <= 0:
            raise GeoAlgorithmExecutionException(
                self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing)))

        if width < hSpacing:
            raise GeoAlgorithmExecutionException(
                self.tr(
                    'Horizontal spacing is too small for the covered area'))

        if height < vSpacing:
            raise GeoAlgorithmExecutionException(
                self.tr('Vertical spacing is too small for the covered area'))

        if self.TYPES[idx].find('polygon') >= 0:
            geometryType = QGis.WKBPolygon
        else:
            geometryType = QGis.WKBLineString

        fields = [
            QgsField('left', QVariant.Double, '', 24, 16),
            QgsField('top', QVariant.Double, '', 24, 16),
            QgsField('right', QVariant.Double, '', 24, 16),
            QgsField('bottom', QVariant.Double, '', 24, 16)
        ]

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fields, geometryType, crs)

        if idx == 0:
            self._rectangleGridLine(writer, width, height, originX, originY,
                                    hSpacing, vSpacing)
        elif idx == 1:
            self._rectangleGridPoly(writer, width, height, originX, originY,
                                    hSpacing, vSpacing)
        elif idx == 2:
            self._diamondGrid(writer, width, height, originX, originY,
                              hSpacing, vSpacing)
        elif idx == 3:
            self._hexagonGrid(writer, width, height, originX, originY,
                              hSpacing, vSpacing)

        del writer
Example #8
0
    def testDimensions(self):
        rect = QgsRectangle(0.0, 0.0, 10.0, 20.0)
        self.assertEqual(rect.width(), 10.0)
        self.assertEqual(rect.height(), 20.0)
        self.assertEqual(rect.center(), QgsPointXY(5.0, 10.0))

        rect.scale(2.0)
        self.assertEqual(rect.width(), 20.0)
        self.assertEqual(rect.height(), 40.0)
Example #9
0
    def testDimensions(self):
        rect = QgsRectangle(0.0, 0.0, 10.0, 10.0)

        myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.width())
        assert rect.width() == 10.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.height())
        assert rect.height() == 10.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % ("5.0, 5.0", rect.center().toString())
        assert rect.center() == QgsPoint(5.0, 5.0), myMessage

        rect.scale(2.0)

        myMessage = "Expected: %s\nGot: %s\n" % (20.0, rect.width())
        assert rect.width() == 20.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (20.0, rect.height())
        assert rect.height() == 20.0, myMessage
Example #10
0
    def testDimensions(self):
        rect = QgsRectangle(0.0, 0.0, 10.0, 10.0)

        myMessage = ('Expected: %s\nGot: %s\n' % (10.0, rect.width()))
        assert rect.width() == 10.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (10.0, rect.height()))
        assert rect.height() == 10.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' %
                     ("5.0, 5.0", rect.center().toString()))
        assert rect.center() == QgsPointXY(5.0, 5.0), myMessage

        rect.scale(2.0)

        myMessage = ('Expected: %s\nGot: %s\n' % (20.0, rect.width()))
        assert rect.width() == 20.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (20.0, rect.height()))
        assert rect.height() == 20.0, myMessage
Example #11
0
    def subrectangle(self, norm_rect, y_inverted=False):
        """
        args:
          norm_rect  -- QgsRectangle (0 <= xmin, 0 <= ymin, xmax <= 1, ymax <= 1)
          y_inverted -- If True, lower-left is (0, 1) and upper-right is (1, 0).
                        Or else lower-left is (0, 0) and upper-right is (1, 1).
        """
        ur_rect = self._unrotated_rect
        xmin = ur_rect.xMinimum() + norm_rect.xMinimum() * ur_rect.width()
        xmax = ur_rect.xMinimum() + norm_rect.xMaximum() * ur_rect.width()
        if y_inverted:
            ymin = ur_rect.yMaximum() - norm_rect.yMaximum() * ur_rect.height()
            ymax = ur_rect.yMaximum() - norm_rect.yMinimum() * ur_rect.height()
        else:
            ymin = ur_rect.yMinimum() + norm_rect.yMinimum() * ur_rect.height()
            ymax = ur_rect.yMinimum() + norm_rect.yMaximum() * ur_rect.height()

        rect = QgsRectangle(xmin, ymin, xmax, ymax)
        return RotatedRect(rect.center(), rect.width(), rect.height()).rotate(self._rotation, self._center)
Example #12
0
  def subrectangle(self, norm_rect, y_inverted=False):
    """
    args:
      norm_rect  -- QgsRectangle (0 <= xmin, 0 <= ymin, xmax <= 1, ymax <= 1)
      y_inverted -- If True, lower-left is (0, 1) and upper-right is (1, 0).
                    Or else lower-left is (0, 0) and upper-right is (1, 1).
    """
    ur_rect = self._unrotated_rect
    xmin = ur_rect.xMinimum() + norm_rect.xMinimum() * ur_rect.width()
    xmax = ur_rect.xMinimum() + norm_rect.xMaximum() * ur_rect.width()
    if y_inverted:
      ymin = ur_rect.yMaximum() - norm_rect.yMaximum() * ur_rect.height()
      ymax = ur_rect.yMaximum() - norm_rect.yMinimum() * ur_rect.height()
    else:
      ymin = ur_rect.yMinimum() + norm_rect.yMinimum() * ur_rect.height()
      ymax = ur_rect.yMinimum() + norm_rect.yMaximum() * ur_rect.height()

    rect = QgsRectangle(xmin, ymin, xmax, ymax)
    return RotatedRect(rect.center(), rect.width(), rect.height()).rotate(self._rotation, self._center)
Example #13
0
def set_zoom(iface, extra_extent):
    """Sets the zoom level to include all layers (excluding tiles layer) with some extra extent

    Parameters
    ----------
    iface: QGIS interface
        The QGIS iface module.
    extra_extent: float
        How much extra space around the layers eg. 1.1 is 10% extra
    """
    zoom_extent = QgsRectangle()
    for layer in QgsProject.instance().mapLayers().values():
        if 'xyz&url' not in layer.source():
            zoom_extent.combineExtentWith(layer.extent())
    if zoom_extent.center().x() != 0.0:
        wgsCRS = QgsCoordinateReferenceSystem(4326)
        QgsProject.instance().setCrs(wgsCRS)
        zoom_extent.scale(extra_extent)
        iface.mapCanvas().setExtent(zoom_extent)
        iface.mapCanvas().refresh()
        wgsCRS = QgsCoordinateReferenceSystem(3857)
        QgsProject.instance().setCrs(wgsCRS)
Example #14
0
    def import_params(self):
        setting_file = QFileDialog.getOpenFileName(
            self, self.tr("Open settings file"), self.lastSavingPath, "*.txt")
        if setting_file[0] != '':
            with open(setting_file[0]) as json_file:
                try:
                    parameters = json.load(json_file)

                    param_crs = QgsCoordinateReferenceSystem()
                    param_crs.createFromProj4(parameters["crs_map"])
                    if (self.map_crs != param_crs):
                        # do traslation
                        transform = QgsCoordinateTransform(
                            param_crs, self.map_crs, QgsProject.instance())
                        pointMin = transform.transform(parameters["roi_x_min"],
                                                       parameters["roi_y_min"])
                        pointMax = transform.transform(parameters["roi_x_max"],
                                                       parameters["roi_y_max"])
                        self.roi_x_max = pointMax.x()
                        self.roi_y_min = pointMin.y()
                        self.roi_x_min = pointMin.x()
                        self.roi_y_max = pointMax.y()
                    else:
                        self.roi_x_max = parameters["roi_x_max"]
                        self.roi_y_min = parameters["roi_y_min"]
                        self.roi_x_min = parameters["roi_x_min"]
                        self.roi_y_max = parameters["roi_y_max"]

                    self.ui.XMaxLineEdit.setText(str(round(self.roi_x_max, 3)))
                    self.ui.YMinLineEdit.setText(str(round(self.roi_y_min, 3)))
                    self.ui.XMinLineEdit.setText(str(round(self.roi_x_min, 3)))
                    self.ui.YMaxLineEdit.setText(str(round(self.roi_y_max, 3)))

                    if "roi_rect_Param" in parameters:
                        self.rect_Params = parameters["roi_rect_Param"]
                    else:
                        rec = QgsRectangle(self.roi_x_min, self.roi_y_min,
                                           self.roi_x_max, self.roi_y_max)
                        self.rect_Params = {
                            'center': [rec.center().x(),
                                       rec.center().y()],
                            'width': rec.width(),
                            'height': rec.height(),
                            'rotation': 0
                        }

                    self.ui.WidthGeoLineEdit.setText(
                        str(round(self.rect_Params["width"], 3)))
                    self.ui.HeightGeoLineEdit.setText(
                        str(round(self.rect_Params["height"], 3)))

                    self.ui.SpacingLineEdit.setText(
                        str(round(parameters["spacing_mm"], 2)))
                    self.scale = parameters['scale']
                    self.scale_h = parameters['scale_h']
                    self.scale_w = parameters['scale_w']
                    self.ui.ScaleLineEdit.setScale(int(parameters["scale"]))
                    self.upload_size_from_scale()
                    self.ui.ZScaleDoubleSpinBox.setValue(parameters["z_scale"])

                    self.get_z_max_z_min()
                    self.ui.BaseHeightLineEdit.setText(
                        str(round(parameters["z_base"], 3)))
                    self.ui.RevereseZCheckBox.setChecked(parameters["z_inv"])
                    self.get_height_model()

                    if "divideRow" in parameters:
                        self.ui.RowPartsSpinBox.setValue(
                            int(parameters["divideRow"]))
                    if "divideCols" in parameters:
                        self.ui.ColPartsSpinBox.setValue(
                            int(parameters["divideCols"]))

                    self.paint_extent(self.rect_Params)

                except:
                    QMessageBox.warning(self, self.tr("Attention"),
                                        self.tr("Wrong file"))
Example #15
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

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

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

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

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

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

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

    # load template
    main_template_folder = impact_report.metadata.template_folder

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

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

    document = QtXml.QDomDocument()

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

    document.setContent(template_content)

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

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

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

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

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

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

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

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

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

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

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

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

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

            composer_map.zoomToExtent(square_extent)
            composer_map.invalidateCache()

            actual_extent = composer_map.extent()

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

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

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

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

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

    # process to output

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

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

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

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

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

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

    component.output = component_output

    return component.output
Example #16
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

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

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

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

    :return: whatever type of output the component should be

    .. versionadded:: 4.0
    """
    context = component.context
    """:type: safe.report.extractors.composer.QGISComposerContext"""
    qgis_composition_context = impact_report.qgis_composition_context
    inasafe_context = impact_report.inasafe_context

    # load composition object
    composition = QgsComposition(qgis_composition_context.map_settings)

    # load template
    main_template_folder = impact_report.metadata.template_folder
    template_path = os.path.join(main_template_folder, component.template)

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

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

    load_status = composition.loadFromTemplate(
        document, context.substitution_map)

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

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerPicture"""
        if image is not None and path is not None:
            try:
                image.setPicturePath(path)
            except:
                pass

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        html_element = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerHtml"""
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsComposerHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsComposerHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

    # resize map extent
    for map_el in context.map_elements:
        item_id = map_el.get('id')
        split_count = map_el.get('grid_split_count')
        layers = map_el.get('layers')
        map_extent_option = map_el.get('extent')
        composer_map = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerMap"""
        if isinstance(composer_map, QgsComposerMap):
            composer_map.setKeepLayerSet(True)
            layer_set = [l.id() for l in layers if isinstance(l, QgsMapLayer)]
            composer_map.setLayerSet(layer_set)
            if map_extent_option and isinstance(
                    map_extent_option, QgsRectangle):
                # use provided map extent
                extent = map_extent_option
            else:
                # if map extent not provided, try to calculate extent
                # from list of given layers. Combine it so all layers were
                # shown properly
                extent = QgsRectangle()
                extent.setMinimal()
                for l in layers:
                    # combine extent if different layer is provided.
                    extent.combineExtentWith(l.extent())

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

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

            composer_map.zoomToExtent(square_extent)
            composer_map.renderModeUpdateCachedImage()

            actual_extent = composer_map.extent()

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

    # calculate legend element
    for leg_el in context.map_legends:
        item_id = leg_el.get('id')
        title = leg_el.get('title')
        layers = leg_el.get('layers')
        symbol_count = leg_el.get('symbol_count')
        column_count = leg_el.get('column_count')

        legend = composition.getComposerItemById(item_id)
        """:type: qgis.core.QgsComposerLegend"""
        if isinstance(legend, QgsComposerLegend):
            # set column count
            if column_count:
                legend.setColumnCount(column_count)
            elif symbol_count <= 5:
                legend.setColumnCount(1)
            else:
                legend.setColumnCount(symbol_count / 5 + 1)

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

            # set legend
            root_group = legend.modelV2().rootGroup()
            for l in layers:
                # used for customizations
                tree_layer = root_group.addLayer(l)
                QgsLegendRenderer.setNodeLegendStyle(
                    tree_layer, QgsComposerLegendStyle.Hidden)
            legend.synchronizeWithModel()

    # process to output

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

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

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

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

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

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

    component.output = component_output

    return component.output
Example #17
0
def qgis_composer_renderer(impact_report, component):
    """Default Map Report Renderer using QGIS Composer.

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

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

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

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

    .. versionadded:: 4.0
    """
    context = component.context
    """:type: safe.report.extractors.composer.QGISComposerContext"""
    qgis_composition_context = impact_report.qgis_composition_context

    # load composition object
    composition = QgsComposition(qgis_composition_context.map_settings)

    # load template
    main_template_folder = impact_report.metadata.template_folder

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

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

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

    load_status = composition.loadFromTemplate(
        document, context.substitution_map)

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

    # replace image path
    for img in context.image_elements:
        item_id = img.get('id')
        path = img.get('path')
        image = composition_item(composition, item_id, QgsComposerPicture)
        """:type: qgis.core.QgsComposerPicture"""
        if image and path:
            image.setPicturePath(path)

    # replace html frame
    for html_el in context.html_frame_elements:
        item_id = html_el.get('id')
        mode = html_el.get('mode')
        composer_item = composition.getComposerItemById(item_id)
        try:
            html_element = composition.getComposerHtmlByItem(composer_item)
        except:
            pass
        """:type: qgis.core.QgsComposerHtml"""
        if html_element:
            if mode == 'text':
                text = html_el.get('text')
                text = text if text else ''
                html_element.setContentMode(QgsComposerHtml.ManualHtml)
                html_element.setHtml(text)
                html_element.loadHtml()
            elif mode == 'url':
                url = html_el.get('url')
                html_element.setContentMode(QgsComposerHtml.Url)
                qurl = QUrl.fromLocalFile(url)
                html_element.setUrl(qurl)

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

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

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

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

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

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

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

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

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

            composer_map.zoomToExtent(square_extent)
            composer_map.renderModeUpdateCachedImage()

            actual_extent = composer_map.extent()

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

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

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

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

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

    # process to output

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

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

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

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

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

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

    component.output = component_output

    return component.output
Example #18
0
def drawDebugInformation(layer, renderContext, zoom, xmin, ymin, xmax, ymax):
  self = layer
  mapSettings = self.iface.mapCanvas().mapSettings()

  lines = []
  lines.append("TileLayer")
  lines.append(" zoom: %d, tile matrix extent: (%d, %d) - (%d, %d), tile count: %d * %d" % (zoom, xmin, ymin, xmax, ymax, xmax - xmin, ymax - ymin))

  extent = renderContext.extent()
  lines.append(" map extent (renderContext): %s" % extent.toString())
  lines.append(" map center (renderContext): %lf, %lf" % (extent.center().x(), extent.center().y()))
  lines.append(" map size: %f, %f" % (extent.width(), extent.height()))
  lines.append(" map extent (map canvas): %s" % self.iface.mapCanvas().extent().toString())

  map2pixel = renderContext.mapToPixel()
  painter = renderContext.painter()
  viewport = painter.viewport()
  mapExtent = QgsRectangle(map2pixel.toMapCoordinatesF(0, 0), map2pixel.toMapCoordinatesF(viewport.width(), viewport.height()))
  lines.append(" map extent (calculated): %s" % mapExtent.toString())
  lines.append(" map center (calc rect): %lf, %lf" % (mapExtent.center().x(), mapExtent.center().y()))

  center = map2pixel.toMapCoordinatesF(0.5 * viewport.width(), 0.5 * viewport.height())
  lines.append(" map center (calc pt): %lf, %lf" % (center.x(), center.y()))

  lines.append(" viewport size (pixel): %d, %d" % (viewport.width(), viewport.height()))
  lines.append(" window size (pixel): %d, %d" % (painter.window().width(), painter.window().height()))
  lines.append(" outputSize (pixel): %d, %d" % (mapSettings.outputSize().width(), mapSettings.outputSize().height()))

  device = painter.device()
  lines.append(" deviceSize (pixel): %f, %f" % (device.width(), device.height()))
  lines.append(" logicalDpi: %f, %f" % (device.logicalDpiX(), device.logicalDpiY()))
  lines.append(" outputDpi: %f" % mapSettings.outputDpi())
  lines.append(" mapToPixel: %s" % map2pixel.showParameters())

  mupp = map2pixel.mapUnitsPerPixel()
  lines.append(" map units per pixel: %f" % mupp)
  lines.append(" meters per pixel (renderContext): %f" % (extent.width() / viewport.width()))
  transform = renderContext.coordinateTransform()
  if transform:
    mpp = mupp * {QGis.Feet: 0.3048, QGis.Degrees: self.layerDef.TSIZE1 / 180}.get(transform.destCRS().mapUnits(), 1)
    lines.append(" meters per pixel (calc 1): %f" % mpp)

    cx, cy = 0.5 * viewport.width(), 0.5 * viewport.height()
    geometry = QgsGeometry.fromPolyline([map2pixel.toMapCoordinatesF(cx - 0.5, cy), map2pixel.toMapCoordinatesF(cx + 0.5, cy)])
    geometry.transform(QgsCoordinateTransform(transform.destCRS(), transform.sourceCrs()))    # project CRS to layer CRS (EPSG:3857)
    mpp = geometry.length()
    lines.append(" meters per pixel (calc center pixel): %f" % mpp)

  lines.append(" scaleFactor: %f" % renderContext.scaleFactor())
  lines.append(" rendererScale: %f" % renderContext.rendererScale())

  scaleX, scaleY = self.getScaleToVisibleExtent(renderContext)
  lines.append(" scale: %f, %f" % (scaleX, scaleY))

  # draw information
  textRect = painter.boundingRect(QRect(QPoint(0, 0), viewport.size()), Qt.AlignLeft, "Q")
  for i, line in enumerate(lines):
    painter.drawText(10, (i + 1) * textRect.height(), line)
    self.log(line)

  # diagonal
  painter.drawLine(QPointF(0, 0), QPointF(painter.viewport().width(), painter.viewport().height()))
  painter.drawLine(QPointF(painter.viewport().width(), 0), QPointF(0, painter.viewport().height()))

  # credit label
  margin, paddingH, paddingV = (3, 4, 3)
  credit = "This is credit"
  rect = QRect(0, 0, painter.viewport().width() - margin, painter.viewport().height() - margin)
  textRect = painter.boundingRect(rect, Qt.AlignBottom | Qt.AlignRight, credit)
  bgRect = QRect(textRect.left() - paddingH, textRect.top() - paddingV, textRect.width() + 2 * paddingH, textRect.height() + 2 * paddingV)
  painter.drawRect(bgRect)
  painter.drawText(rect, Qt.AlignBottom | Qt.AlignRight, credit)
Example #19
0
def drawDebugInformation(layer, renderContext, zoom, xmin, ymin, xmax, ymax):
    self = layer
    mapSettings = self.iface.mapCanvas().mapSettings()

    lines = []
    lines.append("TileLayer")
    lines.append(
        " zoom: %d, tile matrix extent: (%d, %d) - (%d, %d), tile count: %d * %d"
        % (zoom, xmin, ymin, xmax, ymax, xmax - xmin, ymax - ymin))

    extent = renderContext.extent()
    lines.append(" map extent (renderContext): %s" % extent.toString())
    lines.append(" map center (renderContext): %lf, %lf" %
                 (extent.center().x(), extent.center().y()))
    lines.append(" map size: %f, %f" % (extent.width(), extent.height()))
    lines.append(" map extent (map canvas): %s" %
                 self.iface.mapCanvas().extent().toString())

    map2pixel = renderContext.mapToPixel()
    painter = renderContext.painter()
    viewport = painter.viewport()
    mapExtent = QgsRectangle(
        map2pixel.toMapCoordinatesF(0, 0),
        map2pixel.toMapCoordinatesF(viewport.width(), viewport.height()))
    lines.append(" map extent (calculated): %s" % mapExtent.toString())
    lines.append(" map center (calc rect): %lf, %lf" %
                 (mapExtent.center().x(), mapExtent.center().y()))

    center = map2pixel.toMapCoordinatesF(0.5 * viewport.width(),
                                         0.5 * viewport.height())
    lines.append(" map center (calc pt): %lf, %lf" % (center.x(), center.y()))

    lines.append(" viewport size (pixel): %d, %d" %
                 (viewport.width(), viewport.height()))
    lines.append(" window size (pixel): %d, %d" %
                 (painter.window().width(), painter.window().height()))
    lines.append(
        " outputSize (pixel): %d, %d" %
        (mapSettings.outputSize().width(), mapSettings.outputSize().height()))

    device = painter.device()
    lines.append(" deviceSize (pixel): %f, %f" %
                 (device.width(), device.height()))
    lines.append(" logicalDpi: %f, %f" %
                 (device.logicalDpiX(), device.logicalDpiY()))
    lines.append(" outputDpi: %f" % mapSettings.outputDpi())
    lines.append(" mapToPixel: %s" % map2pixel.showParameters())

    mupp = map2pixel.mapUnitsPerPixel()
    lines.append(" map units per pixel: %f" % mupp)
    lines.append(" meters per pixel (renderContext): %f" %
                 (extent.width() / viewport.width()))
    transform = renderContext.coordinateTransform()
    if transform:
        mpp = mupp * {
            QGis.Feet: 0.3048,
            QGis.Degrees: self.layerDef.TSIZE1 / 180
        }.get(transform.destCRS().mapUnits(), 1)
        lines.append(" meters per pixel (calc 1): %f" % mpp)

        cx, cy = 0.5 * viewport.width(), 0.5 * viewport.height()
        geometry = QgsGeometry.fromPolyline([
            map2pixel.toMapCoordinatesF(cx - 0.5, cy),
            map2pixel.toMapCoordinatesF(cx + 0.5, cy)
        ])
        geometry.transform(
            QgsCoordinateTransform(
                transform.destCRS(),
                transform.sourceCrs()))  # project CRS to layer CRS (EPSG:3857)
        mpp = geometry.length()
        lines.append(" meters per pixel (calc center pixel): %f" % mpp)

    lines.append(" scaleFactor: %f" % renderContext.scaleFactor())
    lines.append(" rendererScale: %f" % renderContext.rendererScale())

    scaleX, scaleY = self.getScaleToVisibleExtent(renderContext)
    lines.append(" scale: %f, %f" % (scaleX, scaleY))

    # draw information
    textRect = painter.boundingRect(QRect(QPoint(0, 0), viewport.size()),
                                    Qt.AlignLeft, "Q")
    for i, line in enumerate(lines):
        painter.drawText(10, (i + 1) * textRect.height(), line)
        self.log(line)

    # diagonal
    painter.drawLine(
        QPointF(0, 0),
        QPointF(painter.viewport().width(),
                painter.viewport().height()))
    painter.drawLine(QPointF(painter.viewport().width(), 0),
                     QPointF(0,
                             painter.viewport().height()))

    # credit label
    margin, paddingH, paddingV = (3, 4, 3)
    credit = "This is credit"
    rect = QRect(0, 0,
                 painter.viewport().width() - margin,
                 painter.viewport().height() - margin)
    textRect = painter.boundingRect(rect, Qt.AlignBottom | Qt.AlignRight,
                                    credit)
    bgRect = QRect(textRect.left() - paddingH,
                   textRect.top() - paddingV,
                   textRect.width() + 2 * paddingH,
                   textRect.height() + 2 * paddingV)
    painter.drawRect(bgRect)
    painter.drawText(rect, Qt.AlignBottom | Qt.AlignRight, credit)