Skip to content

render

render

RenderRunner

Bases: MapRunner

Responsible for rendering a QslRenderJob to an image.

Source code in src/qgis_server_light/worker/runner/render.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class RenderRunner(MapRunner):
    """Responsible for rendering a QslRenderJob to an image."""

    job_info_class = QslJobInfoRender

    def __init__(
        self,
        qgis: QgsApplication,
        context: JobContext,
        job_info: QslJobInfoRender,
        layer_cache: Optional[Dict] = None,
    ) -> None:
        super().__init__(qgis, context, job_info, layer_cache)

    @classmethod
    def image_formats(cls):
        return {"image/png": cls._encode_png, "image/jpeg": cls._encode_jpg}

    def run(self):
        """Run this runner.
        Returns:
            A JobResult with the content_type and image_data (bytes) of the rendered image.
        """
        logging.info(f"Executing job: {self.job_info}")
        feature_filter = QgsFeatureFilter()
        for job_layer_definition in self.job_info.job.layers:
            self._provide_layer(job_layer_definition)
        map_settings = self._get_map_settings(self.map_layers)
        filter_providers = QgsFeatureFilterProviderGroup()
        filter_providers.addProvider(feature_filter)
        renderer = QgsMapRendererParallelJob(map_settings)
        renderer.setFeatureFilterProvider(filter_providers)
        event_loop = QEventLoop(self.qgis)
        renderer.finished.connect(event_loop.quit)
        renderer.start()
        event_loop.exec_()
        img = renderer.renderedImage()
        img.setDotsPerMeterX(int(map_settings.outputDpi() * 39.37))
        img.setDotsPerMeterY(int(map_settings.outputDpi() * 39.37))
        content_type, image_data = self._encode_image(img, self.job_info.job.format)
        return JobResult(
            id=self.job_info.id, data=image_data, content_type=content_type
        )

    def _encode_image(self, image: QImage, fmt: str) -> Tuple[str, bytearray]:
        """Encodes an image in a specific mime type
        Args:
            image (QImage): The image to encode
            fmt (str): The mime type of the format
        Returns:
            A tuple with mime type and bytes-like object of an encoded image in the desired format
        """
        try:
            fmt = fmt.lower()
            encoding_method = self.image_formats()[fmt]
            return fmt, encoding_method(image)
        except KeyError:
            raise RuntimeError(
                f"Requested mimtype '{fmt}' was found in {list(self.image_formats.keys())}."
            )

    @staticmethod
    def _encode_png(image: QImage):
        image.convertTo(QImage.Format_RGBA8888)
        image_data = fpng_encode_image_to_memory(
            image.constBits().asstring(image.sizeInBytes()),
            image.width(),
            image.height(),
            0,
            CompressionFlags.NONE,
        )
        return image_data

    @staticmethod
    def _encode_jpg(image: QImage):
        image_data = QByteArray()
        buf = QBuffer(image_data)
        buf.open(QIODevice.WriteOnly)
        image.save(buf, "JPG")
        return image_data

job_info_class = QslJobInfoRender class-attribute instance-attribute

__init__(qgis: QgsApplication, context: JobContext, job_info: QslJobInfoRender, layer_cache: Optional[Dict] = None) -> None

Source code in src/qgis_server_light/worker/runner/render.py
20
21
22
23
24
25
26
27
def __init__(
    self,
    qgis: QgsApplication,
    context: JobContext,
    job_info: QslJobInfoRender,
    layer_cache: Optional[Dict] = None,
) -> None:
    super().__init__(qgis, context, job_info, layer_cache)

image_formats() classmethod

Source code in src/qgis_server_light/worker/runner/render.py
29
30
31
@classmethod
def image_formats(cls):
    return {"image/png": cls._encode_png, "image/jpeg": cls._encode_jpg}

run()

Run this runner. Returns: A JobResult with the content_type and image_data (bytes) of the rendered image.

Source code in src/qgis_server_light/worker/runner/render.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def run(self):
    """Run this runner.
    Returns:
        A JobResult with the content_type and image_data (bytes) of the rendered image.
    """
    logging.info(f"Executing job: {self.job_info}")
    feature_filter = QgsFeatureFilter()
    for job_layer_definition in self.job_info.job.layers:
        self._provide_layer(job_layer_definition)
    map_settings = self._get_map_settings(self.map_layers)
    filter_providers = QgsFeatureFilterProviderGroup()
    filter_providers.addProvider(feature_filter)
    renderer = QgsMapRendererParallelJob(map_settings)
    renderer.setFeatureFilterProvider(filter_providers)
    event_loop = QEventLoop(self.qgis)
    renderer.finished.connect(event_loop.quit)
    renderer.start()
    event_loop.exec_()
    img = renderer.renderedImage()
    img.setDotsPerMeterX(int(map_settings.outputDpi() * 39.37))
    img.setDotsPerMeterY(int(map_settings.outputDpi() * 39.37))
    content_type, image_data = self._encode_image(img, self.job_info.job.format)
    return JobResult(
        id=self.job_info.id, data=image_data, content_type=content_type
    )