def testRenderPage(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) # get width/height, create image and render the composition to it size = QSize(1122, 794) output_image = QImage(size, QImage.Format_RGB32) output_image.setDotsPerMeterX(self.dots_per_meter) output_image.setDotsPerMeterY(self.dots_per_meter) QgsMultiRenderChecker.drawBackground(output_image) painter = QPainter(output_image) exporter = QgsLayoutExporter(l) # valid page exporter.renderPage(painter, 0) painter.end() rendered_file_path = os.path.join(self.basetestpath, 'test_renderpage.png') output_image.save(rendered_file_path, "PNG") self.assertTrue(self.checkImage('renderpage', 'renderpage', rendered_file_path))
def testRenderRegion(self): l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a guide, to ensure it is not included in export g1 = QgsLayoutGuide(Qt.Horizontal, QgsLayoutMeasurement(15, QgsUnitTypes.LayoutMillimeters), l.pageCollection().page(0)) l.guides().addGuide(g1) # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) # get width/height, create image and render the composition to it size = QSize(560, 509) output_image = QImage(size, QImage.Format_RGB32) output_image.setDotsPerMeterX(self.dots_per_meter) output_image.setDotsPerMeterY(self.dots_per_meter) QgsMultiRenderChecker.drawBackground(output_image) painter = QPainter(output_image) exporter = QgsLayoutExporter(l) exporter.renderRegion(painter, QRectF(5, 10, 110, 100)) painter.end() rendered_file_path = os.path.join(self.basetestpath, 'test_renderregion.png') output_image.save(rendered_file_path, "PNG") self.assertTrue(self.checkImage('renderregion', 'renderregion', rendered_file_path))
def testLayout(self, page=0, pixelDiff=0): if self.layout is None: myMessage = "Layout not valid" return False, myMessage # load expected image self.setControlName("expected_" + self.test_name) # get width/height, create image and render the composition to it outputImage = QImage(self.size, QImage.Format_RGB32) outputImage.setDotsPerMeterX(self.dots_per_meter) outputImage.setDotsPerMeterY(self.dots_per_meter) QgsMultiRenderChecker.drawBackground(outputImage) p = QPainter(outputImage) self.layout.exporter().renderPage(p, page) p.end() renderedFilePath = QDir.tempPath() + QDir.separator() + QFileInfo(self.test_name).baseName() + "_rendered.png" outputImage.save(renderedFilePath, "PNG") self.setRenderedImage(renderedFilePath) testResult = self.runTest(self.test_name, pixelDiff) return testResult, self.report()
def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", self.tr("Successfully exported model as image to <a href=\"{}\">{}</a>").format(QUrl.fromLocalFile(filename).toString(), QDir.toNativeSeparators(filename)), level=Qgis.Success, duration=5) self.repaintModel(controls=True)
def exportAsImage(self): filename = unicode(QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)'))) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = QRectF(0, 0, 1, 1) for item in self.scene.items(): totalRect = totalRect.united(item.sceneBoundingRect()) totalRect.adjust(-10, -10, 10, 10) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, totalRect, totalRect) painter.end() img.save(filename)
def runTestForLayer(self, layer, testname): tempdir = tempfile.mkdtemp() layer = QgsVectorLayer(layer, 'Layer', 'ogr') QgsProject.instance().addMapLayer(layer) self.iface.mapCanvas().setExtent(layer.extent()) geom = next(layer.getFeatures()).geometry() highlight = QgsHighlight(self.iface.mapCanvas(), geom, layer) color = QColor(Qt.red) highlight.setColor(color) highlight.setWidth(2) color.setAlpha(50) highlight.setFillColor(color) highlight.show() image = QImage(QSize(400, 400), QImage.Format_ARGB32) image.fill(Qt.white) painter = QPainter() painter.begin(image) self.iface.mapCanvas().render(painter) painter.end() control_image = os.path.join(tempdir, 'highlight_{}.png'.format(testname)) image.save(control_image) checker = QgsRenderChecker() checker.setControlPathPrefix("highlight") checker.setControlName("expected_highlight_{}".format(testname)) checker.setRenderedImage(control_image) self.assertTrue(checker.compareImages("highlight_{}".format(testname))) shutil.rmtree(tempdir)
def exportAsImage(self): self.repaintModel(controls=False) filename, fileFilter = QFileDialog.getSaveFileName(self, self.tr('Save Model As Image'), '', self.tr('PNG files (*.png *.PNG)')) if not filename: return if not filename.lower().endswith('.png'): filename += '.png' totalRect = self.scene.itemsBoundingRect() totalRect.adjust(-10, -10, 10, 10) imgRect = QRectF(0, 0, totalRect.width(), totalRect.height()) img = QImage(totalRect.width(), totalRect.height(), QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) painter = QPainter() painter.setRenderHint(QPainter.Antialiasing) painter.begin(img) self.scene.render(painter, imgRect, totalRect) painter.end() img.save(filename) self.bar.pushMessage("", "Model was correctly exported as image", level=Qgis.Success, duration=5) self.repaintModel(controls=True)
def updateMask(control_image_path, rendered_image_path, mask_image_path): control_image = imageFromPath(control_image_path) if not control_image: error('Could not read control image {}'.format(control_image_path)) rendered_image = imageFromPath(rendered_image_path) if not rendered_image: error('Could not read rendered image {}'.format(rendered_image_path)) if not rendered_image.width() == control_image.width() or not rendered_image.height() == control_image.height(): print ('Size mismatch - control image is {}x{}, rendered image is {}x{}'.format(control_image.width(), control_image.height(), rendered_image.width(), rendered_image.height())) max_width = min(rendered_image.width(), control_image.width()) max_height = min(rendered_image.height(), control_image.height()) #read current mask, if it exist mask_image = imageFromPath(mask_image_path) if mask_image.isNull(): print 'Mask image does not exist, creating {}'.format(mask_image_path) mask_image = QImage(control_image.width(), control_image.height(), QImage.Format_ARGB32) mask_image.fill(QColor(0, 0, 0)) #loop through pixels in rendered image and compare mismatch_count = 0 linebytes = max_width * 4 for y in xrange(max_height): control_scanline = control_image.constScanLine(y).asstring(linebytes) rendered_scanline = rendered_image.constScanLine(y).asstring(linebytes) mask_scanline = mask_image.scanLine(y).asstring(linebytes) for x in xrange(max_width): currentTolerance = qRed(struct.unpack('I', mask_scanline[x * 4:x * 4 + 4])[0]) if currentTolerance == 255: #ignore pixel continue expected_rgb = struct.unpack('I', control_scanline[x * 4:x * 4 + 4])[0] rendered_rgb = struct.unpack('I', rendered_scanline[x * 4:x * 4 + 4])[0] difference = colorDiff(expected_rgb, rendered_rgb) if difference > currentTolerance: #update mask image mask_image.setPixel(x, y, qRgb(difference, difference, difference)) mismatch_count += 1 if mismatch_count: #update mask mask_image.save(mask_image_path, "png") print 'Updated {} pixels in {}'.format(mismatch_count, mask_image_path) else: print 'No mismatches in {}'.format(mask_image_path)
def _get_composer_image(self, width, height, dpi): image = QImage(QSize(width, height), self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) self._c.renderPage(p, 0) p.end() # image = self._c.printPageAsRaster(0) # """:type: QImage""" if image.isNull(): return False, '' filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' return res, filepath
def getCachedImage(self, project, request, key): m = hashlib.md5() paramMap = request.parameters() urlParam = "&".join(["%s=%s" % (k, paramMap[k]) for k in paramMap.keys()]) m.update(urlParam.encode('utf8')) if not os.path.exists(os.path.join(self._tile_cache_dir, m.hexdigest() + ".png")): return QByteArray() img = QImage(m.hexdigest() + ".png") with open(os.path.join(self._tile_cache_dir, m.hexdigest() + ".png"), "rb") as f: statusOK = img.loadFromData(f.read()) if not statusOK: print("Could not read or find the contents document. Error at line %d, column %d:\n%s" % (errorLine, errorColumn, errorStr)) return QByteArray() ba = QByteArray() buff = QBuffer(ba) buff.open(QIODevice.WriteOnly) img.save(buff, 'PNG') return ba
def imageCheck(self, name, reference_image, image): self.report += "<h2>Render {}</h2>\n".format(name) temp_dir = QDir.tempPath() + '/' file_name = temp_dir + 'svg_' + name + ".png" output_image = QImage(image.size(), QImage.Format_RGB32) QgsMultiRenderChecker.drawBackground(output_image) painter = QPainter(output_image) painter.drawImage(0, 0, image) painter.end() output_image.save(file_name, "PNG") checker = QgsRenderChecker() checker.setControlPathPrefix("svg_cache") checker.setControlName("expected_" + reference_image) checker.setRenderedImage(file_name) checker.setColorTolerance(2) result = checker.compareImages(name, 20) self.report += checker.report() print((self.report)) return result
def __signal_pbSaveImg_clicked(self, cheked): if type(self.__fileNameImg) == tuple: self.__fileNameImg = self.__fileNameImg[0] fileName = QFileDialog.getSaveFileName(self, QApplication.translate( "OpenLayersOverviewWidget", "Save image"), self.__fileNameImg, QApplication.translate( "OpenLayersOverviewWidget", "Image(*.jpg)")) if not fileName == '': self.__fileNameImg = fileName else: return img = QImage(self.webViewMap.page().mainFrame().contentsSize(), QImage.Format_ARGB32_Premultiplied) imgPainter = QPainter() imgPainter.begin(img) self.webViewMap.page().mainFrame().render(imgPainter) imgPainter.end() img.save(fileName[0], "JPEG")
def qt_svg_to_png_renderer(impact_report, component): """Render SVG into PNG. :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 filepath = context['filepath'] width = component.extra_args['width'] height = component.extra_args['height'] image_format = QImage.Format_ARGB32 qimage = QImage(width, height, image_format) qimage.fill(0x00000000) renderer = QSvgRenderer(filepath) painter = QPainter(qimage) renderer.render(painter) # Should call painter.end() so that QImage is not used painter.end() # in case output folder not specified if impact_report.output_folder is None: impact_report.output_folder = mkdtemp(dir=temp_dir()) output_path = impact_report.component_absolute_output_path( component.key) qimage.save(output_path) component.output = output_path return component.output
def _svg_to_png(svg_file_path, rendered_file_path, width): svgr = QSvgRenderer(svg_file_path) height = width / svgr.viewBoxF().width() * svgr.viewBoxF().height() image = QImage(width, height, QImage.Format_ARGB32) image.fill(Qt.transparent) p = QPainter(image) p.setRenderHint(QPainter.Antialiasing, False) svgr.render(p) p.end() res = image.save(rendered_file_path, 'png') if not res: os.unlink(rendered_file_path)
def _get_composer_svg_image(self, width, height, dpi): # from qgscomposer.cpp, QgsComposer::on_mActionExportAsSVG_triggered, # near end of function svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) self._c.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing)) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath
def _get_composer_svg_image(self, width, height, dpi): # from qgscomposer.cpp, QgsComposer::on_mActionExportAsSVG_triggered, # near end of function svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) self._c.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath
def saveSpritesSheet(icons, folder): sprites = {} for iconPath, sl in icons.items(): iconName = os.path.splitext(os.path.basename(iconPath))[0] sprites[iconName] = saveSymbolLayerSprite(sl) if sprites: height = max([s.height() for s, s2x in sprites.values()]) width = sum([s.width() for s, s2x in sprites.values()]) img = QImage(width, height, QImage.Format_ARGB32) img.fill(QColor(Qt.transparent)) img2x = QImage(width * 2, height * 2, QImage.Format_ARGB32) img2x.fill(QColor(Qt.transparent)) painter = QPainter(img) painter.begin(img) painter2x = QPainter(img2x) painter2x.begin(img2x) spritesheet = { NO_ICON: { "width": 0, "height": 0, "x": 0, "y": 0, "pixelRatio": 1 } } spritesheet2x = { NO_ICON: { "width": 0, "height": 0, "x": 0, "y": 0, "pixelRatio": 1 } } x = 0 for name, _sprites in sprites.items(): s, s2x = _sprites painter.drawImage(x, 0, s) painter2x.drawImage(x * 2, 0, s2x) spritesheet[name] = { "width": s.width(), "height": s.height(), "x": x, "y": 0, "pixelRatio": 1 } spritesheet2x[name] = { "width": s2x.width(), "height": s2x.height(), "x": x * 2, "y": 0, "pixelRatio": 2 } x += s.width() painter.end() painter2x.end() img.save(os.path.join(folder, "spriteSheet.png")) img2x.save(os.path.join(folder, "*****@*****.**")) with open(os.path.join(folder, "spriteSheet.json"), 'w') as f: json.dump(spritesheet, f) with open(os.path.join(folder, "*****@*****.**"), 'w') as f: json.dump(spritesheet2x, f)
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_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)
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)
def update_summary_sheet(self,lyr=None, force=None): ''' Creates a summary sheet with thumbnail, layer metadata and online view link ''' #create a layer snapshot and upload it to google drive if not lyr: lyr = self.lyr mapbox_style = self.service_sheet.sheet_cell('settings!A5') if not mapbox_style: logger("migrating mapbox style") self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr)) if not force and not self.dirty and not self.restyled: return if self.restyled: self.service_sheet.set_style_qgis(self.layer_style_to_xml(self.lyr)) self.service_sheet.set_style_sld(self.SLD_to_xml(self.lyr)) self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr)) self.saveMetadataState() canvas = QgsMapCanvas() canvas.resize(QSize(600,600)) canvas.setCanvasColor(Qt.white) canvas.setExtent(lyr.extent()) canvas.setLayers([lyr]) canvas.refresh() canvas.update() settings = canvas.mapSettings() settings.setLayers([lyr]) job = QgsMapRendererParallelJob(settings) job.start() job.waitForFinished() image = job.renderedImage() transparent_image = QImage(image.width(), image.height(), QImage.Format_ARGB32) transparent_image.fill(Qt.transparent) p = QPainter(transparent_image) mask = image.createMaskFromColor(QColor(255, 255, 255).rgb(), Qt.MaskInColor) p.setClipRegion(QRegion(QBitmap(QPixmap.fromImage(mask)))) p.drawPixmap(0, 0, QPixmap.fromImage(image)) p.end() tmp_path = os.path.join(self.parent.plugin_dir,self.service_sheet.name+".png") transparent_image.save(tmp_path,"PNG") image_istances = self.service_drive.list_files(mimeTypeFilter='image/png',filename=self.service_sheet.name+".png") for imagename, image_props in image_istances.items(): self.service_drive.delete_file(image_props['id']) result = self.service_drive.upload_image(tmp_path) self.service_drive.add_permission(result['id'],'anyone','reader') webLink = result['webContentLink'] #'https://drive.google.com/uc?export=view&id='+result['id'] logger("webLink:" + webLink) canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326)) worldfile = QgsMapSettingsUtils.worldFileContent(settings) lonlat_min = self.transformToWGS84(QgsPointXY(canvas.extent().xMinimum(), canvas.extent().yMinimum())) lonlat_max = self.transformToWGS84(QgsPointXY(canvas.extent().xMaximum(), canvas.extent().yMaximum())) keymap_extent = [lonlat_min.x(),lonlat_min.y(),lonlat_max.x(),lonlat_max.y()] os.remove(tmp_path) #update layer metadata summary_id = self.service_sheet.add_sheet('summary', no_grid=True) appPropsUpdate = [ ["keymap",webLink], ["worldfile",pack(worldfile)], ["keymap_extent", json.dumps(keymap_extent)] ] res = self.service_sheet.update_appProperties(self.spreadsheet_id,appPropsUpdate) self.saveMetadataState(metadata=self.get_layer_metadata()) self.parent.public_db.setKey(self.spreadsheet_id, dict(self.get_layer_metadata()+appPropsUpdate),only_update=True) #merge cells to visualize snapshot and aaply image snapshot request_body = { 'requests': [{ 'mergeCells': { "range": { "sheetId": summary_id, "startRowIndex": 9, "endRowIndex": 32, "startColumnIndex": 0, "endColumnIndex": 9, }, "mergeType": 'MERGE_ALL' } }] } self.service_sheet.service.spreadsheets().batchUpdate(spreadsheetId=self.spreadsheet_id, body=request_body).execute() self.service_sheet.set_sheet_cell('summary!A10','=IMAGE("%s",3)' % webLink) permissions = self.service_drive.file_property(self.spreadsheet_id,'permissions') for permission in permissions: if permission['type'] == 'anyone': public = True break else: public = False if public: update_range = 'summary!A9:B9' update_body = { "range": update_range, "values": [['public link', "https://enricofer.github.io/gdrive_provider/weblink/converter.html?spreadsheet_id="+self.spreadsheet_id]] } self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=update_range, body=update_body, valueInputOption='USER_ENTERED').execute() #hide worksheets except summary sheets = self.service_sheet.get_sheets() #self.service_sheet.toggle_sheet('summary', sheets['summary'], hidden=None) for sheet_name,sheet_id in sheets.items(): if not sheet_name == 'summary': self.service_sheet.toggle_sheet(sheet_name, sheet_id, hidden=True)
def toSpriteSheet(allSprites): if allSprites: height = spriteSize width = spriteSize * len(allSprites) img = QImage(width, height, QImage.Format_ARGB32) img.fill(QColor(Qt.transparent)) img2x = QImage(width * 2, height * 2, QImage.Format_ARGB32) img2x.fill(QColor(Qt.transparent)) painter = QPainter(img) painter.begin(img) painter2x = QPainter(img2x) painter2x.begin(img2x) spritesheet = { NO_ICON: { "width": 0, "height": 0, "x": 0, "y": 0, "pixelRatio": 1 } } spritesheet2x = { NO_ICON: { "width": 0, "height": 0, "x": 0, "y": 0, "pixelRatio": 1 } } x = 0 for name, _sprites in allSprites.items(): s = _sprites["image"] s2x = _sprites["image2x"] painter.drawImage(x, 0, s) painter2x.drawImage(x * 2, 0, s2x) spritesheet[name] = { "width": s.width(), "height": s.height(), "x": x, "y": 0, "pixelRatio": 1 } spritesheet2x[name] = { "width": s2x.width(), "height": s2x.height(), "x": x * 2, "y": 0, "pixelRatio": 2 } x += s.width() painter.end() painter2x.end() folder = "/Users/ddd/delme/" img.save(os.path.join(folder, "spriteSheet.png")) img2x.save(os.path.join(folder, "*****@*****.**")) with open(os.path.join(folder, "spriteSheet.json"), 'w') as f: json.dump(spritesheet, f) with open(os.path.join(folder, "*****@*****.**"), 'w') as f: json.dump(spritesheet2x, f) return { "img": img, "img2x": img2x, "json": json.dumps(spritesheet), "json2x": json.dumps(spritesheet2x) } return None
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
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)
def printMap(self,taxon): # copy style from grid layer to output layer outstyle = tempfile.gettempdir() + os.sep + "output.qml" getLayerFromId(self.GRID_LAYER).saveNamedStyle(outstyle) self.TAXON_GRID_LAYER.loadNamedStyle(outstyle) # create layer set baseLayer = getLayerFromId(self.BASE_LAYER) if self.TAXON_GRID_LAYER.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") baseCrs = baseLayer.crs() if self.SECONDARY_LAYER != "None": secondaryLayer = getLayerFromId(self.SECONDARY_LAYER) if secondaryLayer.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") else: secondaryLayer = None if self.SURFACE_LAYER != "None": surfaceLayer = getLayerFromId(self.SURFACE_LAYER) if surfaceLayer.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") else: surfaceLayer = None lst = [] lst.append(self.TAXON_GRID_LAYER) if self.SURFACE_LAYER != "None": lst.append(surfaceLayer) if self.SECONDARY_LAYER != "None": lst.append(secondaryLayer) lst.append(baseLayer) ms = QgsMapSettings() ms.setLayers(lst) ms.setBackgroundColor(self.BACKGROUND_COLOUR) # set extent (xmin,ymin,xmax,ymax) rect = QgsRectangle(self.X_MIN,self.Y_MIN,self.X_MAX,self.Y_MAX) ms.setExtent(rect) # set output size outputSize = QSize(self.OUT_WIDTH,self.OUT_HEIGHT) ms.setOutputSize(outputSize) # create painter p = QPainter() p.setRenderHint(QPainter.Antialiasing) # create image (dimensions 325x299) img = QImage(outputSize, QImage.Format_ARGB32_Premultiplied) p.begin(img) # do the rendering r = QgsMapRendererCustomPainterJob(ms, p) r.start() r.waitForFinished() p.end() # save image outdir = self.OUT_DIR img.save(outdir+os.sep+str(str(taxon))+".png","png")