def print_atlas(project, layout_name, feature_filter, scales=None, scale=None): """Generate an atlas. :param project: The project to render as atlas. :type project: QgsProject :param layout_name: Name of the layout. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) if not master_layout: raise AtlasPrintException('Layout not found') if master_layout.layoutType() != QgsMasterLayoutInterface.PrintLayout: raise AtlasPrintException('The layout is not a print layout') for l in manager.printLayouts(): if l.name() == layout_name: layout = l break else: raise AtlasPrintException('The layout is not found') atlas = layout.atlas() if not atlas.enabled(): raise AtlasPrintException('The layout is not enabled for an atlas') settings = QgsLayoutExporter.PdfExportSettings() if scale: layout.referenceMap().setAtlasScalingMode(QgsLayoutItemMap.Fixed) layout.referenceMap().setScale(scale) if scales: layout.referenceMap().setAtlasScalingMode(QgsLayoutItemMap.Predefined) if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = scales else: layout.reportContext().setPredefinedScales(scales) layer = atlas.coverageLayer() feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( 'Expression is invalid, parser error: {}'.format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope(QgsExpressionContextUtils.layoutScope(layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( 'Expression is invalid, eval error: {}'.format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) if not scales and layout.referenceMap().atlasScalingMode( ) == QgsLayoutItemMap.Predefined: if Qgis.QGIS_VERSION_INT >= 30900: use_project = project.useProjectScales() map_scales = project.mapScales() else: map_scales = project_scales(project) use_project = len(map_scales) == 0 if not use_project or len(map_scales) == 0: QgsMessageLog.logMessage( 'Map scales not found in project, fetching predefined map scales in global config', 'atlasprint', Qgis.Info) map_scales = global_scales() if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = map_scales else: layout.reportContext().setPredefinedScales(map_scales) export_path = os.path.join(tempfile.gettempdir(), '{}_{}.pdf'.format(layout_name, uuid4())) exporter = QgsLayoutExporter(layout) result = exporter.exportToPdf(atlas, export_path, settings) if result[0] != QgsLayoutExporter.Success and not os.path.isfile( export_path): raise Exception('export not generated {}'.format(export_path)) return export_path
def exportLayout(self, cView, folder, title): """Function that sets how to export files.""" currProject = QgsProject.instance() printer = QPrinter() painter = QPainter() exporter = QgsLayoutExporter(cView) # Set page progressbar maximum value self.dlg.pageBar.setValue(0) self.dlg.pageBar.setMaximum(11) # Do the export process if not os.path.exists(os.path.join(folder, title)): os.makedirs(os.path.join(folder, title)) exporter.exportToPdf(os.path.join(folder, title, title + '.pdf'), QgsLayoutExporter.PdfExportSettings()) exporter.exportToImage(os.path.join(folder, title, title + '.jpg'), QgsLayoutExporter.ImageExportSettings()) # read CSV file & load into list with open(os.path.join(self.plugin_dir, "input/metadata_items.csv"), 'r') as metadata_file: reader = csv.reader(metadata_file, delimiter=',') metadata_list = list(reader) settings = ET.Element("mapdoc") mapdata = ET.SubElement(settings, "mapdata") # output fixed QGIS variables to XML """ACTION?""" # output project variables listed in CSV to XML for x in metadata_list: ma_variable = str(x[0]) elem_name = str(x[1]) elem_name = elem_name.strip() ma_level = str(x[2]) ma_level = ma_level.strip() if (ma_level == 'project') and (elem_name != 'no_xml'): elem_value = str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable)) ET.SubElement(mapdata, elem_name).text = elem_value if elem_value.strip(): QgsMessageLog.logMessage( ma_variable + ' exported as ' + elem_value, 'MapExport', Qgis.Info) else: msgBar.pushMessage( 'Warning: missing value for ' + ma_variable, 5) QgsMessageLog.logMessage( 'Warning: missing value for ' + ma_variable, 'MapExport') themes = ET.SubElement(mapdata, "themes") for theme in self.dlg.themeBox.findChildren(QCheckBox): if theme.isChecked(): ET.SubElement(themes, 'theme').text = theme.text() for layout in QgsProject.instance().layoutManager().printLayouts(): # Set values of internal variables if layout.name() == self.dlg.layoutSelect.currentText(): date_now = datetime.date.today().strftime("%B %d, %Y") ET.SubElement(mapdata, 'lastUpdated').text = date_now title = layout.name() ET.SubElement(mapdata, 'jpgfilename').text = layout.name() + '.jpg' ET.SubElement(mapdata, 'pdffilename').text = layout.name() + '.pdf' # Action: Which map is selected by this? item = layout.referenceMap() # item = layout.itemById('main') # Get the attr by name and call map_scale = getattr(item, 'scale')() ET.SubElement(mapdata, 'scale').text = str(round(map_scale)) map_extent = item.extent() map_xmin = map_extent.xMinimum() map_xmax = map_extent.xMaximum() map_ymin = map_extent.yMinimum() map_ymax = map_extent.yMaximum() QgsMessageLog.logMessage('Scale ' + str(map_xmin), 'MapExport', Qgis.Info) ET.SubElement(mapdata, 'xmin').text = str(round(map_xmin)) ET.SubElement(mapdata, 'xmax').text = str(round(map_xmax)) ET.SubElement(mapdata, 'ymin').text = str(round(map_ymin)) ET.SubElement(mapdata, 'ymax').text = str(round(map_ymax)) for x in metadata_list: ma_variable = str(x[0]) elem_name = str(x[1]) elem_name = elem_name.strip() ma_level = str(x[2]) ma_level = ma_level.strip() if ma_level == 'layout': elem_value = str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable)) ET.SubElement(mapdata, elem_name).text = elem_value if elem_value.strip(): QgsMessageLog.logMessage( ma_variable + ' exported as ' + elem_value, 'MapExport', Qgis.Info) else: msgBar.pushMessage( 'Warning: missing value for ' + ma_variable, 5) QgsMessageLog.logMessage( 'Warning: missing value for ' + ma_variable, 'MapExport') tree = ET.ElementTree(settings) tree.write(os.path.join(folder, title, title + '.xml')) # Set the location and the file name of the zip zippath = os.path.join(folder, title) zf = zipfile.ZipFile( os.path.abspath(folder) + os.sep + title + ".zip", "w") for dirnames, folders, files in os.walk(os.path.join(folder, title)): # for root, dirs, files in os.walk(folder): for file in files: zf.write(os.path.join(os.path.join(folder, title), file), file) zf.close() self.pageProcessed()
def print_layout(project, layout_name, feature_filter: str = None, scales=None, scale=None, **kwargs): """Generate a PDF for an atlas or a report. :param project: The QGIS project. :type project: QgsProject :param layout_name: Name of the layout of the atlas or report. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. This is required to print atlas, not report :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) settings = QgsLayoutExporter.PdfExportSettings() atlas = None atlas_layout = None report_layout = None logger = Logger() if not master_layout: raise AtlasPrintException('Layout `{}` not found'.format(layout_name)) if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout: for _print_layout in manager.printLayouts(): if _print_layout.name() == layout_name: atlas_layout = _print_layout break atlas = atlas_layout.atlas() if not atlas.enabled(): raise AtlasPrintException('The layout is not enabled for an atlas') layer = atlas.coverageLayer() if feature_filter is None: raise AtlasPrintException( 'EXP_FILTER is mandatory to print an atlas layout') feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( 'Expression is invalid, parser error: {}'.format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope( QgsExpressionContextUtils.layoutScope(atlas_layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( 'Expression is invalid, eval error: {}'.format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) if scale: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Fixed) atlas_layout.referenceMap().setScale(scale) if scales: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Predefined) if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = scales else: atlas_layout.reportContext().setPredefinedScales(scales) if not scales and atlas_layout.referenceMap().atlasScalingMode( ) == QgsLayoutItemMap.Predefined: if Qgis.QGIS_VERSION_INT >= 30900: use_project = project.useProjectScales() map_scales = project.mapScales() else: map_scales = project_scales(project) use_project = len(map_scales) == 0 if not use_project or len(map_scales) == 0: logger.info( 'Map scales not found in project, fetching predefined map scales in global config' ) map_scales = global_scales() if Qgis.QGIS_VERSION_INT >= 30900: settings.predefinedMapScales = map_scales else: atlas_layout.reportContext().setPredefinedScales(map_scales) elif master_layout.layoutType() == QgsMasterLayoutInterface.Report: report_layout = master_layout else: raise AtlasPrintException('The layout is not supported by the plugin') for key, value in kwargs.items(): found = False if atlas_layout: item = atlas_layout.itemById(key.lower()) if isinstance(item, QgsLayoutItemLabel): item.setText(value) found = True logger.info( 'Additional parameters: {} found in layout {}, value {}'.format( key, found, value)) export_path = os.path.join(tempfile.gettempdir(), '{}_{}.pdf'.format(layout_name, uuid4())) result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout, export_path, settings) if result != QgsLayoutExporter.Success and not os.path.isfile(export_path): raise Exception('export not generated {} ({})'.format( export_path, error)) return export_path
def populateMetadataItems(self, m, layout): """Get the current value of metadata items from the project or layout variable, and populate the Edit Metadata dialog See *updateVars* to populate variable values from th Edit Metadata UI """ currProject = QgsProject.instance() # Check that all the variables exist, and if they don't, create them and give them a default value where appropriate with open(os.path.join(self.plugin_dir, "input/metadata_items.csv"), 'r') as metadata_file: reader = csv.reader(metadata_file, delimiter=',') metadata_list = list(reader) for x in metadata_list: ma_variable = str(x[0]) elem_name = str(x[1]) elem_name = elem_name.strip() ma_level = str(x[2]) default_value = str(x[3]) ma_level = ma_level.strip() # If project level variables don't exist, create them and populate with the default value if (ma_level == 'project'): if str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable)) == 'None': QgsExpressionContextUtils.setProjectVariable( currProject, ma_variable, default_value) # Populate comboboxes # Populate values of Status combobox self.dlg.maStatus.addItem('New') self.dlg.maStatus.addItem('Correction') self.dlg.maStatus.addItem('Update') # Populate values of Access combobox self.dlg.maAccess.addItem('Public') self.dlg.maAccess.addItem('MapAction') self.dlg.maAccess.addItem('Selected partners') # Populate values of Language combobox self.dlg.maLanguage.addItem('English') self.dlg.maLanguage.addItem('French') self.dlg.maLanguage.addItem('Spanish') # Populate the Edit Metadata dialog with current values from variables for x in m: ma_variable = str(x[0]) elem_name = str(x[1]) elem_name = elem_name.strip() ma_level = str(x[2]) ma_level = ma_level.strip() default_value = str(x[3]) proj_crs = QgsProject.instance().crs() if (ma_level == 'project'): # Project: get current value for each variable from project and populate field if ma_variable == 'ma_country': self.dlg.maCountry.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_crs': self.dlg.maCrs.setText( str( QgsCoordinateReferenceSystem( proj_crs).description())) elif ma_variable == 'ma_glide_number': self.dlg.maGlide.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_organisation': self.dlg.maOrganisation.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_opid': self.dlg.maOperationID.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_sourceorg': self.dlg.maSourceOrg.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_acknowledgements': self.dlg.maAcknowledgements.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) elif ma_variable == 'ma_disclaimer': self.dlg.maDisclaimer.setText( str( QgsExpressionContextUtils.projectScope( currProject).variable(ma_variable))) # Layout: Get the current value of the variable if it exists and populate the field elif (ma_level == 'layout'): for layout in QgsProject.instance().layoutManager( ).printLayouts(): # If the variable doesn't exist, create it if layout.name() == self.dlg.layoutSelect.currentText(): if str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable)) == 'None': QgsExpressionContextUtils.setLayoutVariable( layout, ma_variable, default_value) elif ma_variable == 'ma_map_number': self.dlg.maMapNumber.setText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_summary': self.dlg.maSummary.setText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_datasource': self.dlg.maDatasource.setText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_title': self.dlg.maTitle.setText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_version': self.dlg.maVersion.setValue( int( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_status': self.dlg.maStatus.setCurrentText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_access': self.dlg.maAccess.setCurrentText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) elif ma_variable == 'ma_language': self.dlg.maLanguage.setCurrentText( str( QgsExpressionContextUtils.layoutScope( layout).variable(ma_variable))) else: # ACTION: update or remove this message QgsMessageLog.logMessage('Warning: level ' + str(ma_level), 'MapExport')
def print_layout( project: QgsProject, layout_name: str, output_format: OutputFormat, feature_filter: str = None, scales: list = None, scale: int = None, **kwargs, ): """Generate a PDF for an atlas or a report. :param project: The QGIS project. :type project: QgsProject :param layout_name: Name of the layout of the atlas or report. :type layout_name: basestring :param feature_filter: QGIS Expression to use to select the feature. It can return many features, a multiple pages PDF will be returned. This is required to print atlas, not report :type feature_filter: basestring :param scale: A scale to force in the atlas context. Default to None. :type scale: int :param scales: A list of predefined list of scales to force in the atlas context. Default to None. :type scales: list :param output_format: The output format, default to PDF if not provided. :return: Path to the PDF. :rtype: basestring """ canvas = QgsMapCanvas() bridge = QgsLayerTreeMapCanvasBridge(project.layerTreeRoot(), canvas) bridge.setCanvasLayers() manager = project.layoutManager() master_layout = manager.layoutByName(layout_name) if output_format == OutputFormat.Svg: settings = QgsLayoutExporter.SvgExportSettings() elif output_format in (OutputFormat.Png, OutputFormat.Jpeg): settings = QgsLayoutExporter.ImageExportSettings() else: # PDF by default settings = QgsLayoutExporter.PdfExportSettings() atlas = None atlas_layout = None report_layout = None logger = Logger() if not master_layout: raise AtlasPrintException("Layout `{}` not found".format(layout_name)) if master_layout.layoutType() == QgsMasterLayoutInterface.PrintLayout: for _print_layout in manager.printLayouts(): if _print_layout.name() == layout_name: atlas_layout = _print_layout break atlas = atlas_layout.atlas() if not atlas.enabled(): raise AtlasPrintException("The layout is not enabled for an atlas") layer = atlas.coverageLayer() if feature_filter is None: raise AtlasPrintException( "EXP_FILTER is mandatory to print an atlas layout") feature_filter = optimize_expression(layer, feature_filter) expression = QgsExpression(feature_filter) if expression.hasParserError(): raise AtlasPrintException( "Expression is invalid, parser error: {}".format( expression.parserErrorString())) context = QgsExpressionContext() context.appendScope(QgsExpressionContextUtils.globalScope()) context.appendScope(QgsExpressionContextUtils.projectScope(project)) context.appendScope( QgsExpressionContextUtils.layoutScope(atlas_layout)) context.appendScope(QgsExpressionContextUtils.atlasScope(atlas)) context.appendScope(QgsExpressionContextUtils.layerScope(layer)) expression.prepare(context) if expression.hasEvalError(): raise AtlasPrintException( "Expression is invalid, eval error: {}".format( expression.evalErrorString())) atlas.setFilterFeatures(True) atlas.setFilterExpression(feature_filter) atlas.updateFeatures() if scale: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Fixed) atlas_layout.referenceMap().setScale(scale) if scales: atlas_layout.referenceMap().setAtlasScalingMode( QgsLayoutItemMap.Predefined) settings.predefinedMapScales = scales if (not scales and atlas_layout.referenceMap().atlasScalingMode() == QgsLayoutItemMap.Predefined): use_project = project.useProjectScales() map_scales = project.mapScales() if not use_project or len(map_scales) == 0: logger.info( "Map scales not found in project, fetching predefined map scales in global config" ) map_scales = global_scales() settings.predefinedMapScales = map_scales elif master_layout.layoutType() == QgsMasterLayoutInterface.Report: report_layout = master_layout else: raise AtlasPrintException("The layout is not supported by the plugin") for key, value in kwargs.items(): found = False if atlas_layout: item = atlas_layout.itemById(key.lower()) if isinstance(item, QgsLayoutItemLabel): item.setText(value) found = True logger.info( 'Additional parameters "{key}" {found} in layout, value "{value}"'. format(key=key, found="found" if found else "not found", value=value)) file_name = "{}_{}.{}".format(clean_string(layout_name), uuid4(), output_format.name.lower()) export_path = Path(tempfile.gettempdir()).joinpath(file_name) Logger().info("Exporting the request in {} using {}".format( export_path, output_format.value)) if output_format in (OutputFormat.Png, OutputFormat.Jpeg): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToImage(str(export_path), settings) error = result_message(result) elif output_format in (OutputFormat.Svg, ): exporter = QgsLayoutExporter(atlas_layout or report_layout) result = exporter.exportToSvg(str(export_path), settings) error = result_message(result) else: # Default to PDF result, error = QgsLayoutExporter.exportToPdf(atlas or report_layout, str(export_path), settings) # Let's override error message _ = error error = result_message(result) if result != QgsLayoutExporter.Success: raise Exception("Export not generated in QGIS exporter {} : {}".format( export_path, error)) if not export_path.is_file(): logger.warning( "No error from QGIS Exporter, but the file does not exist.\n" "Message from QGIS exporter : {}\n" "File path : {}\n".format(error, export_path)) raise Exception( "Export OK from QGIS, but file not found on the file system : {}". format(export_path)) return export_path