def testResizeWithMapContent(self): """Test test legend resizes to match map content""" point_path = os.path.join(TEST_DATA_DIR, 'points.shp') point_layer = QgsVectorLayer(point_path, 'points', 'ogr') QgsProject.instance().addMapLayers([point_layer]) s = QgsMapSettings() s.setLayers([point_layer]) s.setCrsTransformEnabled(False) composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) composer_map.setLayers([point_layer]) composition.addComposerMap(composer_map) composer_map.setNewExtent(point_layer.extent()) legend = QgsComposerLegend(composition) legend.setSceneRect(QRectF(120, 20, 80, 80)) legend.setFrameEnabled(True) legend.setFrameOutlineWidth(2) legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') legend.setLegendFilterByMapEnabled(True) composition.addComposerLegend(legend) legend.setComposerMap(composer_map) composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) checker = QgsCompositionChecker('composer_legend_size_content', composition) checker.setControlPathPrefix("composer_legend") result, message = checker.testComposition() self.assertTrue(result, message) QgsProject.instance().removeMapLayers([point_layer.id()])
def testInitialSizeSymbolMapUnits(self): """Test initial size of legend with a symbol size in map units""" point_path = os.path.join(TEST_DATA_DIR, 'points.shp') point_layer = QgsVectorLayer(point_path, 'points', 'ogr') QgsProject.instance().addMapLayers([point_layer]) marker_symbol = QgsMarkerSymbol.createSimple({'color': '#ff0000', 'outline_style': 'no', 'size': '5', 'size_unit': 'MapUnit'}) point_layer.setRenderer(QgsSingleSymbolRenderer(marker_symbol)) s = QgsMapSettings() s.setLayers([point_layer]) composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) composer_map.setLayers([point_layer]) composition.addComposerMap(composer_map) composer_map.setNewExtent(point_layer.extent()) legend = QgsComposerLegend(composition) legend.setSceneRect(QRectF(120, 20, 80, 80)) legend.setFrameEnabled(True) legend.setFrameStrokeWidth(2) legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') composition.addComposerLegend(legend) legend.setComposerMap(composer_map) checker = QgsCompositionChecker( 'composer_legend_mapunits', composition) checker.setControlPathPrefix("composer_legend") result, message = checker.testComposition() self.assertTrue(result, message) QgsProject.instance().removeMapLayers([point_layer.id()])
def testDuplicateComposition(self): """ Test duplicating compositions """ project = QgsProject() manager = QgsLayoutManager(project) doc = QDomDocument("testdoc") self.assertFalse(manager.duplicateComposition('not in manager', 'dest')) composition = QgsComposition(project) composition.setName('test composition') composition.setPaperSize(100, 200) manager.addComposition(composition) # duplicate name self.assertFalse(manager.duplicateComposition('test composition', 'test composition')) result = manager.duplicateComposition('test composition', 'dupe composition') self.assertTrue(result) # make sure result in stored in manager self.assertEqual(result, manager.compositionByName('dupe composition')) self.assertEqual(result.name(), 'dupe composition') self.assertEqual(result.paperHeight(), 200) self.assertEqual(result.paperWidth(), 100)
def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'rgb256x256.png') rasterFileInfo = QFileInfo(myPath) mRasterLayer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer(mRasterLayer.dataProvider(), 1, 2, 3) mRasterLayer.setRenderer(rasterRenderer) #pipe = mRasterLayer.pipe() #assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsMapLayerRegistry.instance().addMapLayers([mRasterLayer]) # create composition with composer map self.mMapSettings = QgsMapSettings() self.mMapSettings.setLayers([mRasterLayer.id()]) self.mMapSettings.setCrsTransformEnabled(False) self.mComposition = QgsComposition(self.mMapSettings) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mComposerMap)
def createComposition(self): ''' Create a composition ''' # Create composition c = QgsComposition(self.mInstance) # Set main properties c.setPaperSize(self.pageWidth, self.pageHeight) c.setPrintResolution(self.printResolution) c.setSnapGridOffsetX(3.5) c.setSnapGridOffsetY(0) c.setSnapGridResolution(2.5) # Set page number self.getPageNumberNeeded() c.setNumPages(self.numPages) # Set plot style if map added if self.etype == 'parcelle': c.setPlotStyle(QgsComposition.Print) self.currentComposition = c
def testGridNorth(self): """Test syncing picture to grid north""" mapSettings = QgsMapSettings() composition = QgsComposition(mapSettings) composerMap = QgsComposerMap(composition) composerMap.setNewExtent(QgsRectangle(0, -256, 256, 0)) composition.addComposerMap(composerMap) composerPicture = QgsComposerPicture(composition) composition.addComposerPicture(composerPicture) composerPicture.setRotationMap(composerMap.id()) self.assertTrue(composerPicture.rotationMap() >= 0) composerPicture.setNorthMode(QgsComposerPicture.GridNorth) composerMap.setMapRotation(45) self.assertEqual(composerPicture.pictureRotation(), 45) # add an offset composerPicture.setNorthOffset(-10) self.assertEqual(composerPicture.pictureRotation(), 35)
def testCase(self): TEST_DATA_DIR = unitTestDataPath() vectorFileInfo = QFileInfo( TEST_DATA_DIR + "/france_parts.shp") mVectorLayer = QgsVectorLayer( vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr" ) QgsMapLayerRegistry.instance().addMapLayers( [mVectorLayer] ) # create composition with composer map mMapRenderer = QgsMapRenderer() layerStringList = [] layerStringList.append( mVectorLayer.id() ) mMapRenderer.setLayerSet( layerStringList ) mMapRenderer.setProjectionsEnabled( False ) mComposition = QgsComposition( mMapRenderer ) mComposition.setPaperSize( 297, 210 ) mLabel = QgsComposerLabel( mComposition ) mComposition.addComposerLabel( mLabel ) self.evaluation_test( mComposition, mLabel ) self.feature_evaluation_test( mComposition, mLabel, mVectorLayer ) self.page_evaluation_test( mComposition, mLabel, mVectorLayer )
def load_composition(self): """ Creates the composition object (which is needed for creating the file) from the template file. :returns: the composition object :rtype: QgsComposition """ template_path = get_plugin_path() + self.templates[ self.dockwidget.comboBox_template.currentText()] template_file = open(template_path, "r") content = template_file.read() template_file.close() # the method from QgsComposition for loading the template needs to be a QDomDocument document = QDomDocument() document.setContent(content) # composition = QgsComposition(iface.mapCanvas().mapSettings()) does not work #TODO: is deprecated but works ...fix in new version https://hub.qgis.org/issues/11077 composition = QgsComposition(iface.mapCanvas().mapRenderer()) if not composition.loadFromTemplate(document): iface.messageBar().pushMessage("Error while loading template!") return # set map map_item = composition.getComposerItemById("map") map_item.setMapCanvas(iface.mapCanvas()) map_item.zoomToExtent(iface.mapCanvas().extent()) # set legend try: legend_item = composition.getComposerItemById("legend") legend_item.updateLegend() except AttributeError: # in case first template was selected pass composition.refreshItems() return composition
def make_pdf(): canvas = QgsMapCanvas() # Load our project QgsProject.instance().read(QFileInfo(project_path)) bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), canvas) bridge.setCanvasLayers() if canvas.layerCount() < 1: print 'No layers loaded from this project, exiting.' return print canvas.mapSettings().extent().toString() template_file = file(template_path) template_content = template_file.read() template_file.close() document = QDomDocument() document.setContent(template_content) composition = QgsComposition(canvas.mapSettings()) # You can use this to replace any string like this [key] # in the template with a new value. e.g. to replace # [date] pass a map like this {'date': '1 Jan 2012'} substitution_map = { 'DATE_TIME_START': TIME_START, 'DATE_TIME_END': TIME_STOP } composition.loadFromTemplate(document, substitution_map) # You must set the id in the template map_item = composition.getComposerItemById('map') map_item.setMapCanvas(canvas) map_item.zoomToExtent(canvas.extent()) # You must set the id in the template legend_item = composition.getComposerItemById('legend') legend_item.updateLegend() composition.refreshItems() composition.exportAsPDF('/home/web/reports/pdf/%s/%s.pdf' % (TIME_SLICE, LABEL)) QgsProject.instance().clear()
def testTrueNorth(self): """Test syncing picture to true north""" mapSettings = QgsMapSettings() mapSettings.setDestinationCrs( QgsCoordinateReferenceSystem.fromEpsgId(3575)) composition = QgsComposition(mapSettings) composerMap = QgsComposerMap(composition) composerMap.setNewExtent( QgsRectangle(-2126029.962, -2200807.749, -119078.102, -757031.156)) composition.addComposerMap(composerMap) composerPicture = QgsComposerPicture(composition) composition.addComposerPicture(composerPicture) composerPicture.setRotationMap(composerMap.id()) self.assertTrue(composerPicture.rotationMap() >= 0) composerPicture.setNorthMode(QgsComposerPicture.TrueNorth) self.assertAlmostEqual(composerPicture.pictureRotation(), 37.20, 1) # shift map composerMap.setNewExtent( QgsRectangle(2120672.293, -3056394.691, 2481640.226, -2796718.780)) self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18, 1) # rotate map composerMap.setMapRotation(45) self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 45, 1) # add an offset composerPicture.setNorthOffset(-10) self.assertAlmostEqual(composerPicture.pictureRotation(), -38.18 + 35, 1)
# coding: utf-8 from qgis.core import QgsComposition from qgis.gui import QgsComposerRuler from qgis.utils import iface composer_ruler = QgsComposerRuler(QgsComposerRuler.Vertical) composition = QgsComposition(iface.mapCanvas().mapSettings()) composer_ruler.setComposition(composition) composer_ruler.show()
app = QgsApplication(sys.argv, True) QgsApplication.setPrefixPath("/usr", True) QgsApplication.initQgis() project_path = 'report_maps.qgs' template_path = 'owner_occupancy.qpt' canvas = QgsMapCanvas() canvas.resize(QSize(1450, 850)) #start = time.time() QgsProject.instance().read(QFileInfo(project_path)) #end = time.time() root = QgsProject.instance().layerTreeRoot() bridge = QgsLayerTreeMapCanvasBridge(root, canvas) bridge.setCanvasLayers() registry = QgsMapLayerRegistry.instance() template_file = file(template_path) template_content = template_file.read() template_file.close() document = QDomDocument() document.setContent(template_content) map_settings = canvas.mapSettings() composition = QgsComposition(map_settings) #start = time.time() composition.loadFromTemplate(document) #end = time.time() #create list of all layers currently in the map map_layers = [lyr for lyr in registry.mapLayers() if root.findLayer(lyr)]
def generate_report(self): # Generate pdf report from impact/hazard LOGGER.info('Generating report') if not self.impact_exists: # Cannot generate report when no impact layer present LOGGER.info('Cannot Generate report when no impact present.') return project_instance = QgsProject.instance() project_instance.setFileName(self.project_path) project_instance.read() # get layer registry layer_registry = QgsMapLayerRegistry.instance() layer_registry.removeAllMapLayers() # Set up the map renderer that will be assigned to the composition map_renderer = CANVAS.mapRenderer() # Enable on the fly CRS transformations map_renderer.setProjectionsEnabled(True) default_crs = map_renderer.destinationCrs() crs = QgsCoordinateReferenceSystem('EPSG:4326') map_renderer.setDestinationCrs(crs) # add place name layer layer_registry.addMapLayer(self.cities_layer, False) # add airport layer layer_registry.addMapLayer(self.airport_layer, False) # add volcano layer layer_registry.addMapLayer(self.volcano_layer, False) # add impact layer hazard_layer = read_qgis_layer(self.hazard_path, self.tr('People Affected')) layer_registry.addMapLayer(hazard_layer, False) # add basemap layer layer_registry.addMapLayer(self.highlight_base_layer, False) # add basemap layer layer_registry.addMapLayer(self.overview_layer, False) CANVAS.setExtent(hazard_layer.extent()) CANVAS.refresh() template_path = self.ash_fixtures_dir('realtime-ash.qpt') with open(template_path) as f: template_content = f.read() document = QDomDocument() document.setContent(template_content) # Now set up the composition # map_settings = QgsMapSettings() # composition = QgsComposition(map_settings) composition = QgsComposition(map_renderer) subtitution_map = self.event_dict() LOGGER.debug(subtitution_map) # load composition object from template result = composition.loadFromTemplate(document, subtitution_map) if not result: LOGGER.exception('Error loading template %s with keywords\n %s', template_path, subtitution_map) raise MapComposerError # get main map canvas on the composition and set extent map_impact = composition.getComposerItemById('map-impact') if map_impact: map_impact.zoomToExtent(hazard_layer.extent()) map_impact.renderModeUpdateCachedImage() else: LOGGER.exception('Map canvas could not be found in template %s', template_path) raise MapComposerError # get overview map canvas on the composition and set extent map_overall = composition.getComposerItemById('map-overall') if map_overall: map_overall.setLayerSet([self.overview_layer.id()]) # this is indonesia extent indonesia_extent = QgsRectangle(94.0927980005593554, -15.6629591962689343, 142.0261493318861312, 10.7379406374101816) map_overall.zoomToExtent(indonesia_extent) map_overall.renderModeUpdateCachedImage() else: LOGGER.exception('Map canvas could not be found in template %s', template_path) raise MapComposerError # setup impact table self.render_population_table() self.render_nearby_table() self.render_landcover_table() impact_table = composition.getComposerItemById('table-impact') if impact_table is None: message = 'table-impact composer item could not be found' LOGGER.exception(message) raise MapComposerError(message) impacts_html = composition.getComposerHtmlByItem(impact_table) if impacts_html is None: message = 'Impacts QgsComposerHtml could not be found' LOGGER.exception(message) raise MapComposerError(message) impacts_html.setUrl(QUrl(self.population_html_path)) # setup nearby table nearby_table = composition.getComposerItemById('table-nearby') if nearby_table is None: message = 'table-nearby composer item could not be found' LOGGER.exception(message) raise MapComposerError(message) nearby_html = composition.getComposerHtmlByItem(nearby_table) if nearby_html is None: message = 'Nearby QgsComposerHtml could not be found' LOGGER.exception(message) raise MapComposerError(message) nearby_html.setUrl(QUrl(self.nearby_html_path)) # setup landcover table landcover_table = composition.getComposerItemById('table-landcover') if landcover_table is None: message = 'table-landcover composer item could not be found' LOGGER.exception(message) raise MapComposerError(message) landcover_html = composition.getComposerHtmlByItem(landcover_table) if landcover_html is None: message = 'Landcover QgsComposerHtml could not be found' LOGGER.exception(message) raise MapComposerError(message) landcover_html.setUrl(QUrl(self.landcover_html_path)) # setup logos logos_id = ['logo-bnpb', 'logo-geologi'] for logo_id in logos_id: logo_picture = composition.getComposerItemById(logo_id) if logo_picture is None: message = '%s composer item could not be found' % logo_id LOGGER.exception(message) raise MapComposerError(message) pic_path = os.path.basename(logo_picture.picturePath()) pic_path = os.path.join('logo', pic_path) logo_picture.setPicturePath(self.ash_fixtures_dir(pic_path)) # save a pdf composition.exportAsPDF(self.map_report_path) project_instance.write(QFileInfo(self.project_path)) layer_registry.removeAllMapLayers() map_renderer.setDestinationCrs(default_crs) map_renderer.setProjectionsEnabled(False) LOGGER.info('Report generation completed.')
dpi = 200 canvas = QgsMapCanvas() # Load our project QgsProject.instance().read(QFileInfo(project_path)) bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), canvas) bridge.setCanvasLayers() template_file = file(template_path) template_content = template_file.read() template_file.close() document = QDomDocument() document.setContent(template_content) ms = canvas.mapSettings() composition = QgsComposition(ms) composition.loadFromTemplate(document, {}) # You must set the id in the template #map_item = composition.getComposerItemById('map') map_item = composition.getComposerMapById(1) #map_item.setMapCanvas(canvas) #map_item.zoomToExtent(canvas.extent()) # You must set the id in the template #legend_item = composition.getComposerItemById('legend') #legend_item.updateLegend() composition.refreshItems() dpmm = dpi / 25.4 #width = int(dpmm * composition.paperWidth()) width = int(1400) print(width)
def print_atlas(self, project_path, composer_name, predefined_scales, feature_filter=None, page_name_expression=None): # Get composer from project # in QGIS 2, canno get composers without iface # so we reading project xml and extract composer # in QGIS 3.0, we will use project layoutManager() from xml.etree import ElementTree as ET composer_xml = None with open(project_path, 'r') as f: tree = ET.parse(f) for elem in tree.findall('.//Composer[@title="%s"]' % composer_name): composer_xml = ET.tostring(elem, encoding='utf8', method='xml') if not composer_xml: return document = QDomDocument() document.setContent(composer_xml) # Get canvas, map setting & instantiate composition canvas = QgsMapCanvas() QgsProject.instance().read(QFileInfo(project_path)) bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), canvas) bridge.setCanvasLayers() ms = canvas.mapSettings() composition = QgsComposition(ms) # Load content from XML substitution_map = {} composition.loadFromTemplate(document, substitution_map) # Get atlas for this composition atlas = composition.atlasComposition() atlas.setEnabled(True) atlas_map = composition.getComposerMapById(0) atlas_map.setAtlasScalingMode(QgsComposerMap.Predefined) # get project scales atlas.setPredefinedScales(predefined_scales) atlas.setComposerMap(atlas_map) #on definit le filtre if feature_filter: atlas.setFilterFeatures(True) atlas.setFeatureFilter(feature_filter) if page_name_expression: atlas.setPageNameExpression(page_name_expression) # Set atlas mode composition.setAtlasMode(QgsComposition.ExportAtlas) # Generate atlas atlas.beginRender() uid = uuid4() for i in range(0, atlas.numFeatures()): atlas.prepareForFeature(i) export_path = os.path.join( tempfile.gettempdir(), '%s_%s.pdf' % (atlas.nameForPage(i), uid)) exported = composition.exportAsPDF(export_path) if not exported or not os.path.isfile(export_path): return None break atlas.endRender() return export_path
def qgis_composer_html_renderer(impact_report, component): """HTML to PDF renderer using QGIS Composer. Render using qgis composer for a given impact_report data and component context for html input. :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) if not context.html_frame_elements: # if no html frame elements at all, do not generate empty report. component.output = '' return component.output # Add HTML Frame for html_el in context.html_frame_elements: mode = html_el.get('mode') html_element = QgsComposerHtml(composition, False) """:type: qgis.core.QgsComposerHtml""" margin_left = html_el.get('margin_left', 10) margin_top = html_el.get('margin_top', 10) width = html_el.get('width', component.page_width - 2 * margin_left) height = html_el.get('height', component.page_height - 2 * margin_top) html_frame = QgsComposerFrame( composition, html_element, margin_left, margin_top, width, height) html_element.addFrame(html_frame) if html_element: if mode == 'text': text = html_el.get('text') text = text if text else '' html_element.setContentMode(QgsComposerHtml.ManualHtml) html_element.setResizeMode( QgsComposerHtml.RepeatUntilFinished) html_element.setHtml(text) html_element.loadHtml() elif mode == 'url': url = html_el.get('url') html_element.setContentMode(QgsComposerHtml.Url) html_element.setResizeMode( QgsComposerHtml.RepeatUntilFinished) qurl = QUrl.fromLocalFile(url) html_element.setUrl(qurl) # Attempt on removing blank page. Notes: We assume that the blank page # will always appears in the last x page(s), not in the middle. index = composition.numPages() number_of_pages_to_be_removed = 0 while composition.pageIsEmpty(index): number_of_pages_to_be_removed += 1 index -= 1 composition.setNumPages( composition.numPages() - number_of_pages_to_be_removed) # process to output # in case output folder not specified if impact_report.output_folder is None: impact_report.output_folder = mkdtemp(dir=temp_dir()) component_output_path = impact_report.component_absolute_output_path( component.key) component_output = None output_format = component.output_format 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
def testCase(self): self.TEST_DATA_DIR = unitTestDataPath() tmppath = tempfile.mkdtemp() for file in glob.glob( os.path.join(self.TEST_DATA_DIR, 'france_parts.*')): shutil.copy(os.path.join(self.TEST_DATA_DIR, file), tmppath) vectorFileInfo = QFileInfo(tmppath + "/france_parts.shp") mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") QgsMapLayerRegistry.instance().addMapLayers([mVectorLayer]) # create composition with composer map self.mapSettings = QgsMapSettings() layerStringList = [] layerStringList.append(mVectorLayer.id()) self.mapSettings.setLayers(layerStringList) self.mapSettings.setCrsTransformEnabled(True) self.mapSettings.setMapUnits(QGis.Meters) # select epsg:2154 crs = QgsCoordinateReferenceSystem() crs.createFromSrid(2154) self.mapSettings.setDestinationCrs(crs) self.mComposition = QgsComposition(self.mapSettings) self.mComposition.setPaperSize(297, 210) # fix the renderer, fill with green props = {"color": "0,127,0"} fillSymbol = QgsFillSymbolV2.createSimple(props) renderer = QgsSingleSymbolRendererV2(fillSymbol) mVectorLayer.setRendererV2(renderer) # the atlas map self.mAtlasMap = QgsComposerMap(self.mComposition, 20, 20, 130, 130) self.mAtlasMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mAtlasMap) # the atlas self.mAtlas = self.mComposition.atlasComposition() self.mAtlas.setCoverageLayer(mVectorLayer) self.mAtlas.setEnabled(True) self.mComposition.setAtlasMode(QgsComposition.ExportAtlas) # an overview mOverview = QgsComposerMap(self.mComposition, 180, 20, 50, 50) mOverview.setFrameEnabled(True) mOverview.setOverviewFrameMap(self.mAtlasMap.id()) self.mComposition.addComposerMap(mOverview) nextent = QgsRectangle(49670.718, 6415139.086, 699672.519, 7065140.887) mOverview.setNewExtent(nextent) # set the fill symbol of the overview map props2 = {"color": "127,0,0,127"} fillSymbol2 = QgsFillSymbolV2.createSimple(props2) mOverview.setOverviewFrameMapSymbol(fillSymbol2) # header label self.mLabel1 = QgsComposerLabel(self.mComposition) self.mComposition.addComposerLabel(self.mLabel1) self.mLabel1.setText("[% \"NAME_1\" %] area") self.mLabel1.setFont(QgsFontUtils.getStandardTestFont()) self.mLabel1.adjustSizeToText() self.mLabel1.setSceneRect(QRectF(150, 5, 60, 15)) qWarning( "header label font: %s exactMatch:%s" % (self.mLabel1.font().toString(), self.mLabel1.font().exactMatch())) # feature number label self.mLabel2 = QgsComposerLabel(self.mComposition) self.mComposition.addComposerLabel(self.mLabel2) self.mLabel2.setText("# [%$feature || ' / ' || $numfeatures%]") self.mLabel2.setFont(QgsFontUtils.getStandardTestFont()) self.mLabel2.adjustSizeToText() self.mLabel2.setSceneRect(QRectF(150, 200, 60, 15)) qWarning( "feature number label font: %s exactMatch:%s" % (self.mLabel2.font().toString(), self.mLabel2.font().exactMatch())) self.filename_test() self.autoscale_render_test() self.autoscale_render_test_old_api() self.fixedscale_render_test() self.predefinedscales_render_test() self.hidden_render_test() self.legend_test() shutil.rmtree(tmppath, True)
def setUp(self): """Run before each test.""" self.mapSettings = QgsMapSettings() self.mComposition = QgsComposition(self.mapSettings) self.mComposition.setPaperSize(297, 210) #A4 landscape
def exportToPdf(self, print_context, targetFile=None): self.iface.mapCanvas().setDestinationCrs( self.settings_instance.projection.crs()) myComposition = QgsComposition(self.iface.mapCanvas().mapSettings()) template = self.env.get_template(print_context['template']) custom_qpt = template.render(CONTEXT=print_context) myDocument = QDomDocument() myDocument.setContent(custom_qpt) myComposition.loadFromTemplate(myDocument) suggestedFile = os.path.join( self.settings_instance.projectFolderPath.text(), print_context['title'] + ".pdf") if not targetFile: targetFile = QFileDialog.getSaveFileName( None, "Export " + print_context['job'], suggestedFile, "*.pdf") interactive = True if not targetFile: return else: interactive = None outputDir = tempfile.gettempdir() with open(os.path.join(targetFile + '.qpt'), "wb") as qpt_file: qpt_file.write(custom_qpt) if print_context['type'] == 'report': myComposition.exportAsPDF(targetFile) elif print_context['type'] == 'map': print myComposition.getComposerMapById(0) print myComposition.getComposerItemById('5') for composer_map in myComposition.composerMapItems(): print composer_map, composer_map.id() fdtm_extent = None for layer in [ self.settings_instance.EPpLayer, self.settings_instance.EApLayer, self.settings_instance.WRLayer, self.settings_instance.WDSLayer ]: if layer.featureCount() > 0: if fdtm_extent: fdtm_extent.combineExtentWith(layer.extent()) else: fdtm_extent = layer.extent() fdtm_extent.scale(1.1) composer_map.zoomToExtent(fdtm_extent) composer_map.updateItem() myComposition.refreshItems() myComposition.exportAsPDF(targetFile) elif print_context['type'] == 'mapppp': print print_context myComposition = QgsComposition( self.iface.mapCanvas().mapSettings()) myComposition.setPlotStyle(QgsComposition.Print) myComposition.setPaperSize(297, 210) composer_map = QgsComposerMap(myComposition, 10, 10, 190, 190) fdtm_extent = self.settings_instance.EPpLayer.extent() for layer in [ self.settings_instance.EApLayer, self.settings_instance.WRLayer, self.settings_instance.WDSLayer ]: fdtm_extent.combineExtentWith(layer.extent()) composer_map.zoomToExtent(fdtm_extent) composer_map.updateItem() myComposition.addItem(composer_map) table = QgsComposerAttributeTable(myComposition) table.setItemPosition(205, 170) table.setVectorLayer(QgsMapLayerRegistry.instance().mapLayer( print_context['id'])) table.setMaximumNumberOfFeatures(20) table.setFilterFeatures(True) col1 = QgsComposerTableColumn() col1.setAttribute('types') col1.setHeading("types") col2 = QgsComposerTableColumn() col2.setAttribute('project units') col2.setHeading("project units") col3 = QgsComposerTableColumn() col3.setAttribute('Areas') col3.setHeading("Areas") col4 = QgsComposerTableColumn() col4.setAttribute('Lengths') col4.setHeading("Lengths") col5 = QgsComposerTableColumn() col5.setAttribute('Cost') col5.setHeading("Cost") table.setColumns([col1, col2, col3, col4, col5]) myComposition.addItem(table) myComposition.exportAsPDF(targetFile) elif print_context['type'] == 'atlas': myComposition.setAtlasMode(QgsComposition.ExportAtlas) for composer_map in myComposition.composerMapItems(): print composer_map, composer_map.id() atlas = myComposition.atlasComposition() atlas.setComposerMap(composer_map) #DEPRECATED atlas.setPredefinedScales(self.PREDEFINED_SCALES) composer_map.setAtlasDriven(True) composer_map.setAtlasScalingMode(QgsComposerMap.Predefined) atlas.beginRender() rendered_pdf = [] progress = progressBar(self, "exporting " + print_context['job'], atlas.numFeatures()) for i in range(0, atlas.numFeatures()): atlas.prepareForFeature(i) current_filename = atlas.currentFilename() file_name = '_'.join(current_filename.split()) file_path = '%s.pdf' % file_name path = os.path.join(outputDir, file_path) myComposition.exportAsPDF(path) rendered_pdf.append(path) progress.setStep(i) progress.stop(print_context['job'] + "exported to " + targetFile) atlas.endRender() merge_pdfs(rendered_pdf, targetFile) if interactive: open_file(targetFile) return targetFile
def export_all_features(self): pdf_painter = None """Export map to pdf atlas style (one page per feature)""" if VRP_DEBUG is True: QgsMessageLog.logMessage(u'exporting map', DLG_CAPTION) try: result = self.__delete_pdf() if not result is None: return result ids = [] exp = QgsExpression(self.feature_filter) if exp.hasParserError(): raise Exception(exp.parserErrorString()) exp.prepare(self.coverage_layer.pendingFields()) for feature in self.coverage_layer.getFeatures(): value = exp.evaluate(feature) if exp.hasEvalError(): raise ValueError(exp.evalErrorString()) if bool(value): if VRP_DEBUG is True: QgsMessageLog.logMessage(u'export map, feature id:{0}'.format(feature.id()), DLG_CAPTION) ids.append(feature.id()) self.coverage_layer.select(ids) bbox = self.coverage_layer.boundingBoxOfSelected() self.canvas.zoomToSelected(self.coverage_layer) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox:{0}'.format(bbox.toString()), DLG_CAPTION) #self.map_renderer.setExtent(bbox) #self.map_renderer.updateScale() #read plotlayout composition = QgsComposition(self.map_renderer) self.composition = composition composition.setPlotStyle(QgsComposition.Print) error, xml_doc = self.__read_template() if not error is None: return error if composition.loadFromTemplate(xml_doc) is False: return u'Konnte Template nicht laden!\n{0}'.format(self.template_qpt) #read textinfo layout self.comp_textinfo = QgsComposition(self.map_renderer) self.comp_textinfo.setPlotStyle(QgsComposition.Print) error, xml_doc = self.__read_template(True) if not error is None: return error if self.comp_textinfo.loadFromTemplate(xml_doc) is False: return u'Konnte Template nicht laden!\n{0}'.format(self.settings.textinfo_layout()) new_ext = bbox if QGis.QGIS_VERSION_INT > 20200: compmaps = self.__get_items(QgsComposerMap) if len(compmaps) < 1: return u'Kein Kartenfenster im Layout vorhanden!' compmap = compmaps[0] else: if len(composition.composerMapItems()) < 1: return u'Kein Kartenfenster im Layout vorhanden!' compmap = composition.composerMapItems()[0] self.composermap = compmap #self.composermap.setPreviewMode(QgsComposerMap.Render) #self.composermap.setPreviewMode(QgsComposerMap.Rectangle) #taken from QgsComposerMap::setNewAtlasFeatureExtent (not yet available in QGIS 2.0) #http://www.qgis.org/api/qgscomposermap_8cpp_source.html#l00610 old_ratio = compmap.rect().width() / compmap.rect().height() new_ratio = new_ext.width() / new_ext.height() if old_ratio < new_ratio: new_height = new_ext.width() / old_ratio delta_height = new_height - new_ext.height() new_ext.setYMinimum( bbox.yMinimum() - delta_height / 2) new_ext.setYMaximum(bbox.yMaximum() + delta_height / 2) else: new_width = old_ratio * new_ext.height() delta_width = new_width - new_ext.width() new_ext.setXMinimum(bbox.xMinimum() - delta_width / 2) new_ext.setXMaximum(bbox.xMaximum() + delta_width / 2) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox old:{0}'.format(compmap.extent().toString()), DLG_CAPTION) compmap.setNewExtent(new_ext) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox new:{0}'.format(compmap.extent().toString()), DLG_CAPTION) #round up to next 1000 compmap.setNewScale(math.ceil((compmap.scale()/1000.0)) * 1000.0) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'bbox new (after scale):{0}'.format(compmap.extent().toString()), DLG_CAPTION) #add ORTHO after new extent -> performance if not self.ortho is None: self.ortho_lyr = self.__add_raster_layer(self.ortho, self.lyrname_ortho) self.__reorder_layers() self.comp_leg = self.__get_items(QgsComposerLegend) self.comp_lbl = self.__get_items(QgsComposerLabel) self.__update_composer_items(self.settings.dkm_gemeinde(self.gem_name)['lyrnamegstk']) if VRP_DEBUG is True: QgsMessageLog.logMessage(u'paperWidth:{0} paperHeight:{1}'.format(composition.paperWidth(), composition.paperHeight()), DLG_CAPTION) printer = QPrinter() printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(self.pdf_map) printer.setPaperSize(QSizeF(composition.paperWidth(), composition.paperHeight()), QPrinter.Millimeter) printer.setFullPage(True) printer.setColorMode(QPrinter.Color) printer.setResolution(composition.printResolution()) pdf_painter = QPainter(printer) paper_rect_pixel = printer.pageRect(QPrinter.DevicePixel) paper_rect_mm = printer.pageRect(QPrinter.Millimeter) QgsPaintEngineHack.fixEngineFlags(printer.paintEngine()) #DKM only if len(self.themen) < 1: composition.render(pdf_painter, paper_rect_pixel, paper_rect_mm) else: self.statistics = OrderedDict() try: pass #lyr = QgsVectorLayer('/home/bergw/VoGIS-Raumplanung-Daten/Geodaten/Raumplanung/Flaechenwidmung/Dornbirn/Flaechenwidmungsplan/fwp_flaeche.shp', 'flaeiw', 'ogr') #lyr.loadNamedStyle('/home/bergw/VoGIS-Raumplanung-Daten/Geodaten/Raumplanung/Flaechenwidmung/Vorarlberg/Flaechenwidmungsplan/fwp_flaeche.qml') #QgsMapLayerRegistry.instance().addMapLayer(lyr) except: QgsMessageLog.logMessage('new lyr:{0}'.format(sys.exc_info()[0]), DLG_CAPTION) #QgsMapLayerRegistry.instance().addMapLayer(lyr) cntr = 0 for thema, sub_themen in self.themen.iteritems(): if VRP_DEBUG is True: QgsMessageLog.logMessage('drucke Thema:{0}'.format(thema.name), DLG_CAPTION) if sub_themen is None: layers = self.__add_layers(thema) self.__calculate_statistics(thema, thema, layers) #no qml -> not visible -> means no map if self.__at_least_one_visible(layers) is True: if cntr > 0: printer.newPage() self.__reorder_layers() self.__update_composer_items(thema.name, layers=layers) composition.renderPage(pdf_painter, 0) QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) cntr += 1 else: QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) if not sub_themen is None: for sub_thema in sub_themen: if VRP_DEBUG is True: QgsMessageLog.logMessage(u'drucke SubThema:{0}'.format(sub_thema.name), DLG_CAPTION) layers = self.__add_layers(sub_thema) self.__calculate_statistics(thema, sub_thema, layers) #no qml -> not visible -> means no map if self.__at_least_one_visible(layers) is True: if cntr > 0: printer.newPage() self.__reorder_layers() self.__update_composer_items(thema.name, subthema=sub_thema.name, layers=layers) composition.renderPage(pdf_painter, 0) QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) cntr += 1 else: QgsMapLayerRegistry.instance().removeMapLayers([lyr.id() for lyr in layers]) #output statistics if len(self.statistics) > 0: printer.setPaperSize(QSizeF(210, 297), QPrinter.Millimeter) tabelle = self.__get_item_byid(self.comp_textinfo, 'TABELLE') if tabelle is None: self.iface.messageBar().pushMessage(u'Layout (Textinfo): Kein Textelement mit ID "TABELLE" vorhanden.', QgsMessageBar.CRITICAL) else: try: str_flaechen = '' idx = 0 for gnr, stats in self.statistics.iteritems(): comma = ', ' if idx > 0 else '' str_flaechen += u'{0}{1} ({2:.2f}m²)'.format(comma, gnr, stats[0].flaeche) idx += 1 lbls = self.__get_items(QgsComposerLabel, self.comp_textinfo) self.__update_composer_items('', labels=lbls, gnrflaeche=str_flaechen) html = tabelle.text() html += u'<table>' #gnrcnt = 0 for gnr, stats in self.statistics.iteritems(): #if gnrcnt > 0: # html += u'<tr class="abstand"><td> </td><td> </td><td> </td></tr>' html += u'<tr><th class="gnr"></th><th class="gnr">{0}</th><th class="gnr"></th></tr>'.format(gnr) #html += u'<tr class="abstand"><td> </td><td> </td><td> </td></tr>' curr_thema = '' for stat in stats: if stat.thema != curr_thema: html += u'<tr><th class="thema"></th><th class="thema">{0}</th><th class="thema"></th></tr>'.format(stat.thema) curr_thema = stat.thema for thema, subthema in stat.subthemen.iteritems(): for quelle in subthema: html += u'<tr><td class="col1">{0}</td>'.format(quelle.name) attr_val = '' attr_area = '' for text, area in quelle.txt_area.iteritems(): attr_val += u'{0}<br />'.format(text) attr_area += u'{0:.2f}m² <br />'.format(area) html += u'<td class="col2">{0}</td><td class="col3">{1}</td></tr>'.format(attr_val, attr_area) #gnrcnt += 1 html += u'</table>' tabelle.setText(html) printer.newPage() self.comp_textinfo.renderPage(pdf_painter, 0) except: msg = 'Statistikausgabe:\n\n{0}'.format(traceback.format_exc()) QgsMessageLog.logMessage(msg, DLG_CAPTION) self.iface.messageBar().pushMessage(msg, QgsMessageBar.CRITICAL) except: msg = 'export pdf (catch all):\n\n{0}'.format(traceback.format_exc()) QgsMessageLog.logMessage(msg, DLG_CAPTION) self.iface.messageBar().pushMessage(msg.replace(u'\n', u''), QgsMessageBar.CRITICAL) return msg finally: #end pdf if not pdf_painter is None: pdf_painter.end() return None
def generate_report(self): # Generate pdf report from impact/hazard if not self.impact_exists: # Cannot generate report when no impact layer present return project_path = os.path.join(self.report_path, 'project-%s.qgs' % self.locale) project_instance = QgsProject.instance() project_instance.setFileName(project_path) project_instance.read() # Set up the map renderer that will be assigned to the composition map_renderer = CANVAS.mapRenderer() # Set the labelling engine for the canvas labelling_engine = QgsPalLabeling() map_renderer.setLabelingEngine(labelling_engine) # Enable on the fly CRS transformations map_renderer.setProjectionsEnabled(True) default_crs = map_renderer.destinationCrs() crs = QgsCoordinateReferenceSystem('EPSG:4326') map_renderer.setDestinationCrs(crs) # get layer registry layer_registry = QgsMapLayerRegistry.instance() layer_registry.removeAllMapLayers() # add impact layer population_affected_layer = read_qgis_layer( self.population_aggregate_path, self.tr('People Affected')) layer_registry.addMapLayer(population_affected_layer, True) # add boundary mask boundary_mask = read_qgis_layer( self.flood_fixtures_dir('boundary-mask.shp')) layer_registry.addMapLayer(boundary_mask, False) # add hazard layer hazard_layer = read_qgis_layer(self.hazard_path, self.tr('Flood Depth (cm)')) layer_registry.addMapLayer(hazard_layer, True) # add boundary layer boundary_layer = read_qgis_layer( self.flood_fixtures_dir('boundary-5.shp')) layer_registry.addMapLayer(boundary_layer, False) CANVAS.setExtent(boundary_layer.extent()) CANVAS.refresh() # add basemap layer # this code uses OpenlayersPlugin base_map = QgsRasterLayer(self.flood_fixtures_dir('jakarta.jpg')) layer_registry.addMapLayer(base_map, False) CANVAS.refresh() template_path = self.flood_fixtures_dir('realtime-flood.qpt') with open(template_path) as f: template_content = f.read() document = QDomDocument() document.setContent(template_content) # set destination CRS to Jakarta CRS # EPSG:32748 # This allows us to use the scalebar in meter unit scale crs = QgsCoordinateReferenceSystem('EPSG:32748') map_renderer.setDestinationCrs(crs) # Now set up the composition composition = QgsComposition(map_renderer) subtitution_map = self.event_dict() LOGGER.debug(subtitution_map) # load composition object from template result = composition.loadFromTemplate(document, subtitution_map) if not result: LOGGER.exception('Error loading template %s with keywords\n %s', template_path, subtitution_map) raise MapComposerError # get main map canvas on the composition and set extent map_canvas = composition.getComposerItemById('map-canvas') if map_canvas: map_canvas.setNewExtent(map_canvas.currentMapExtent()) map_canvas.renderModeUpdateCachedImage() else: LOGGER.exception('Map canvas could not be found in template %s', template_path) raise MapComposerError # get map legend on the composition map_legend = composition.getComposerItemById('map-legend') if map_legend: # show only legend for Flood Depth # ''.star # showed_legend = [layer_id for layer_id in map_renderer.layerSet() # if layer_id.startswith('Flood_Depth')] # LOGGER.info(showed_legend) # LOGGER.info(map_renderer.layerSet()) # LOGGER.info(hazard_layer.id()) # map_legend.model().setLayerSet(showed_legend) # map_legend.modelV2().clear() # print dir(map_legend.modelV2()) # map_legend.setLegendFilterByMapEnabled(True) map_legend.model().setLayerSet( [hazard_layer.id(), population_affected_layer.id()]) else: LOGGER.exception('Map legend could not be found in template %s', template_path) raise MapComposerError content_analysis = composition.getComposerItemById( 'content-analysis-result') if not content_analysis: message = 'Content analysis composer item could not be found' LOGGER.exception(message) raise MapComposerError(message) content_analysis_html = content_analysis.multiFrame() if content_analysis_html: # set url to generated html analysis_html_path = self.generate_analysis_result_html() # We're using manual HTML to avoid memory leak and segfault # happened when using Url Mode content_analysis_html.setContentMode(QgsComposerHtml.ManualHtml) with open(analysis_html_path) as f: content_analysis_html.setHtml(f.read()) content_analysis_html.loadHtml() else: message = 'Content analysis HTML not found in template' LOGGER.exception(message) raise MapComposerError(message) # save a pdf composition.exportAsPDF(self.map_report_path) project_instance.write(QFileInfo(project_path)) layer_registry.removeAllMapLayers() map_renderer.setDestinationCrs(default_crs) map_renderer.setProjectionsEnabled(False)
def load_template(self, renderer): """Load composer template for merged report. Validate it as well. The template needs to have: 1. QgsComposerMap with id 'impact-map' for merged impact map. 2. QgsComposerPicture with id 'safe-logo' for InaSAFE logo. 3. QgsComposerLabel with id 'summary-report' for a summary of two impacts. 4. QgsComposerLabel with id 'aggregation-area' to indicate the area of aggregation. 5. QgsComposerScaleBar with id 'map-scale' for impact map scale. 6. QgsComposerLegend with id 'map-legend' for impact map legend. 7. QgsComposerPicture with id 'organisation-logo' for organisation logo. 8. QgsComposerLegend with id 'impact-legend' for map legend. 9. QgsComposerHTML with id 'merged-report-table' for the merged report. :param renderer: Map renderer :type renderer: QgsMapRenderer """ # Create Composition composition = QgsComposition(renderer) template_file = QtCore.QFile(self.template_path) template_file.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text) template_content = template_file.readAll() template_file.close() # Create a dom document containing template content document = QtXml.QDomDocument() document.setContent(template_content) # Prepare Map Substitution impact_title = '%s and %s' % (self.first_impact['map_title'], self.second_impact['map_title']) substitution_map = { 'impact-title': impact_title, 'hazard-title': self.first_impact['hazard_title'], 'disclaimer': self.disclaimer } # Load template load_status = composition.loadFromTemplate(document, substitution_map) if not load_status: raise ReportCreationError( self.tr('Error loading template %s') % self.template_path) # Validate all needed composer components component_ids = [ 'impact-map', 'safe-logo', 'summary-report', 'aggregation-area', 'map-scale', 'map-legend', 'organisation-logo', 'merged-report-table' ] for component_id in component_ids: component = composition.getComposerItemById(component_id) if component is None: raise ReportCreationError( self.tr('Component %s could not be found' % component_id)) # Set InaSAFE logo safe_logo = composition.getComposerItemById('safe-logo') safe_logo.setPictureFile(self.safe_logo_path) # set organisation logo org_logo = composition.getComposerItemById('organisation-logo') org_logo.setPictureFile(self.organisation_logo_path) # Set Map Legend legend = composition.getComposerItemById('map-legend') legend.updateLegend() return composition
def run(self, *args, **kwargs): """ :param templatePath: The file path to the user-defined template. :param entityFieldName: The name of the column for the specified entity which must exist in the data source view or table. :param entityFieldValue: The value for filtering the records in the data source view or table. :param outputMode: Whether the output composition should be an image or PDF. :param filePath: The output file where the composition will be written to. Applies to single mode output generation. :param dataFields: List containing the field names whose values will be used to name the files. This is used in multiple mode configuration. :param fileExtension: The output file format. Used in multiple mode configuration. :param dbmodel: In order to name the files using the custom column mapping, a callable sqlalchemy data model must be specified. """ #Unpack arguments templatePath = args[0] entityFieldName = args[1] entityFieldValue = args[2] outputMode = args[3] filePath = kwargs.get("filePath", None) dataFields = kwargs.get("dataFields", []) fileExtension = kwargs.get("fileExtension", "") dataModel = kwargs.get("dbmodel", None) templateFile = QFile(templatePath) if not templateFile.open(QIODevice.ReadOnly): return (False, QApplication.translate("DocumentGenerator", "Cannot read template file.")) templateDoc = QDomDocument() if templateDoc.setContent(templateFile): composerDS = ComposerDataSource.create(templateDoc) spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) composerDS.setSpatialFieldsConfig(spatialFieldsConfig) #Execute query dsTable, records = self._execQuery(composerDS.name(), entityFieldName, entityFieldValue) if records == None: return (False, QApplication.translate( "DocumentGenerator", "No matching records in the database")) """ Iterate through records where a single file output will be generated for each matching record. """ for rec in records: composition = QgsComposition(self._mapRenderer) composition.loadFromTemplate(templateDoc) #Set value of composer items based on the corresponding db values for composerId in composerDS.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = composition.getComposerItemById(composerId) if composerItem != None: fieldName = composerDS.dataFieldName(composerId) fieldValue = getattr(rec, fieldName) self._composerItemValueHandler(composerItem, fieldValue) #Create memory layers for spatial features and add them to the map for mapId, spfmList in spatialFieldsConfig.spatialFieldsMapping( ).iteritems(): mapItem = composition.getComposerItemById(mapId) if mapItem != None: #Clear any previous memory layer self.clearTemporaryLayers() for spfm in spfmList: #Use the value of the label field to name the layer layerName = getattr(rec, spfm.labelField()) #Extract the geometry using geoalchemy spatial capabilities geomFunc = getattr( rec, spfm.spatialField()).ST_AsText() geomWKT = self._dbSession.scalar(geomFunc) #Create reference layer with feature refLayer = self._buildVectorLayer(layerName) #Add feature bbox = self._addFeatureToLayer(refLayer, geomWKT) bbox.scale(spfm.zoomLevel()) #Add layer to map QgsMapLayerRegistry.instance().addMapLayer( refLayer) self._iface.mapCanvas().setExtent(bbox) self._iface.mapCanvas().refresh() #mapItem.storeCurrentLayerSet() #mapItem.updateCachedImage() #Add layer to memory layer list self._memoryLayers.append(refLayer) mapItem.setNewExtent(self._mapRenderer.extent()) #Build output path and generate composition if filePath != None and len(dataFields) == 0: self._writeOutput(composition, outputMode, filePath) elif filePath == None and len(dataFields) > 0: docFileName = self._buildFileName(dataModel, entityFieldName, entityFieldValue, dataFields, fileExtension) if docFileName == "": return ( False, QApplication.translate( "DocumentGenerator", "File name could not be generated from the data fields." )) outputDir = self._composerOutputPath() if outputDir == None: return ( False, QApplication.translate( "DocumentGenerator", "System could not read the location of the output directory in the registry." )) qDir = QDir() if not qDir.exists(outputDir): return (False, QApplication.translate( "DocumentGenerator", "Output directory does not exist")) absDocPath = unicode(outputDir) + "/" + docFileName self._writeOutput(composition, outputMode, absDocPath) #Clear temporary layers self.clearTemporaryLayers() return (True, "Success") return (False, "Composition could not be generated")
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
def run(self, *args, **kwargs): """ :param templatePath: The file path to the user-defined template. :param entityFieldName: The name of the column for the specified entity which must exist in the data source view or table. :param entityFieldValue: The value for filtering the records in the data source view or table. :param outputMode: Whether the output composition should be an image or PDF. :param filePath: The output file where the composition will be written to. Applies to single mode output generation. :param dataFields: List containing the field names whose values will be used to name the files. This is used in multiple mode configuration. :param fileExtension: The output file format. Used in multiple mode configuration. :param data_source: Name of the data source table or view whose row values will be used to name output files if the options has been specified by the user. """ templatePath = args[0] entityFieldName = args[1] entityFieldValue = args[2] outputMode = args[3] filePath = kwargs.get("filePath", None) dataFields = kwargs.get("dataFields", []) fileExtension = kwargs.get("fileExtension", "") data_source = kwargs.get("data_source", "") templateFile = QFile(templatePath) if not templateFile.open(QIODevice.ReadOnly): return False, QApplication.translate("DocumentGenerator", "Cannot read template file.") templateDoc = QDomDocument() if templateDoc.setContent(templateFile): composerDS = ComposerDataSource.create(templateDoc) spatialFieldsConfig = SpatialFieldsConfiguration.create( templateDoc) composerDS.setSpatialFieldsConfig(spatialFieldsConfig) #Check if data source exists and return if it doesn't if not self.data_source_exists(composerDS): msg = QApplication.translate( "DocumentGenerator", u"'{0}' data source does not exist in the database." u"\nPlease contact your database " u"administrator.".format(composerDS.name())) return False, msg #Set file name value formatter self._file_name_value_formatter = EntityValueFormatter( name=data_source) #Register field names to be used for file naming self._file_name_value_formatter.register_columns(dataFields) #TODO: Need to automatically register custom configuration collections #Photo config collection ph_config_collection = PhotoConfigurationCollection.create( templateDoc) #Table configuration collection table_config_collection = TableConfigurationCollection.create( templateDoc) #Create chart configuration collection object chart_config_collection = ChartConfigurationCollection.create( templateDoc) #Load the layers required by the table composer items self._table_mem_layers = load_table_layers(table_config_collection) #Execute query dsTable, records = self._exec_query(composerDS.name(), entityFieldName, entityFieldValue) if records is None or len(records) == 0: return False, QApplication.translate( "DocumentGenerator", "No matching records in the database") """ Iterate through records where a single file output will be generated for each matching record. """ for rec in records: composition = QgsComposition(self._map_renderer) composition.loadFromTemplate(templateDoc) ref_layer = None #Set value of composer items based on the corresponding db values for composerId in composerDS.dataFieldMappings().reverse: #Use composer item id since the uuid is stripped off composerItem = composition.getComposerItemById(composerId) if not composerItem is None: fieldName = composerDS.dataFieldName(composerId) fieldValue = getattr(rec, fieldName) self._composeritem_value_handler( composerItem, fieldValue) # Extract photo information self._extract_photo_info(composition, ph_config_collection, rec) # Set table item values based on configuration information self._set_table_data(composition, table_config_collection, rec) # Refresh non-custom map composer items self._refresh_composer_maps( composition, spatialFieldsConfig.spatialFieldsMapping().keys()) # Create memory layers for spatial features and add them to the map for mapId, spfmList in spatialFieldsConfig.spatialFieldsMapping( ).iteritems(): map_item = composition.getComposerItemById(mapId) if not map_item is None: # #Clear any previous map memory layer #self.clear_temporary_map_layers() for spfm in spfmList: #Use the value of the label field to name the layer lbl_field = spfm.labelField() spatial_field = spfm.spatialField() if not spatial_field: continue if lbl_field: if hasattr(rec, spfm.labelField()): layerName = getattr(rec, spfm.labelField()) else: layerName = self._random_feature_layer_name( spatial_field) else: layerName = self._random_feature_layer_name( spatial_field) #Extract the geometry using geoalchemy spatial capabilities geom_value = getattr(rec, spatial_field) if geom_value is None: continue geom_func = geom_value.ST_AsText() geomWKT = self._dbSession.scalar(geom_func) #Get geometry type geom_type, srid = geometryType( composerDS.name(), spatial_field) #Create reference layer with feature ref_layer = self._build_vector_layer( layerName, geom_type, srid) if ref_layer is None or not ref_layer.isValid(): continue #Add feature bbox = self._add_feature_to_layer( ref_layer, geomWKT) bbox.scale(spfm.zoomLevel()) #Workaround for zooming to single point extent if ref_layer.wkbType() == QGis.WKBPoint: canvas_extent = self._iface.mapCanvas( ).fullExtent() cnt_pnt = bbox.center() canvas_extent.scale(1.0 / 32, cnt_pnt) bbox = canvas_extent #Style layer based on the spatial field mapping symbol layer symbol_layer = spfm.symbolLayer() if not symbol_layer is None: ref_layer.rendererV2().symbols( )[0].changeSymbolLayer(0, spfm.symbolLayer()) ''' Add layer to map and ensure its always added at the top ''' self.map_registry.addMapLayer(ref_layer) self._iface.mapCanvas().setExtent(bbox) self._iface.mapCanvas().refresh() # Add layer to map memory layer list self._map_memory_layers.append(ref_layer.id()) self._hide_layer(ref_layer) ''' Use root layer tree to get the correct ordering of layers in the legend ''' self._refresh_map_item(map_item) #Extract chart information and generate chart self._generate_charts(composition, chart_config_collection, rec) #Build output path and generate composition if not filePath is None and len(dataFields) == 0: self._write_output(composition, outputMode, filePath) elif filePath is None and len(dataFields) > 0: docFileName = self._build_file_name( data_source, entityFieldName, entityFieldValue, dataFields, fileExtension) # Replace unsupported characters in Windows file naming docFileName = docFileName.replace('/', '_').replace \ ('\\', '_').replace(':', '_').strip('*?"<>|') if not docFileName: return ( False, QApplication.translate( "DocumentGenerator", "File name could not be generated from the data fields." )) outputDir = self._composer_output_path() if outputDir is None: return ( False, QApplication.translate( "DocumentGenerator", "System could not read the location of the output directory in the registry." )) qDir = QDir() if not qDir.exists(outputDir): return (False, QApplication.translate( "DocumentGenerator", "Output directory does not exist")) absDocPath = u"{0}/{1}".format(outputDir, docFileName) self._write_output(composition, outputMode, absDocPath) return True, "Success" return False, "Document composition could not be generated"
def print_impact_table(self, output_path): """Pint summary from impact layer to PDF. ..note:: The order of the report: 1. Summary table 2. Aggregation table 3. Attribution table :param output_path: Output path. :type output_path: str :return: Path to generated pdf file. :rtype: str :raises: None """ keywords = self._keyword_io.read_keywords(self.layer) if output_path is None: output_path = unique_filename(suffix='.pdf', dir=temp_dir()) try: impact_template = get_report_template(self.layer.source()) summary_table = impact_template.generate_html_report() except: summary_table = keywords.get('impact_summary', None) full_table = keywords.get('impact_table', None) aggregation_table = keywords.get('postprocessing_report', None) attribution_table = impact_attribution(keywords) # (AG) We will not use impact_table as most of the IF use that as: # impact_table = impact_summary + some information intended to be # shown on screen (see FloodOsmBuilding) # Unless the impact_summary is None, we will use impact_table as the # alternative html = m.Brand().to_html() html += m.Heading(tr('Analysis Results'), **INFO_STYLE).to_html() if summary_table is None: html += full_table else: html += summary_table if aggregation_table is not None: html += aggregation_table if attribution_table is not None: html += attribution_table.to_html() html = html_header() + html + html_footer() # Print HTML using composition # For QGIS < 2.4 compatibility # QgsMapSettings is added in 2.4 if qgis_version() < 20400: map_settings = QgsMapRenderer() else: map_settings = QgsMapSettings() # A4 Portrait # TODO: Will break when we try to use larger print layouts TS paper_width = 210 paper_height = 297 # noinspection PyCallingNonCallable composition = QgsComposition(map_settings) # noinspection PyUnresolvedReferences composition.setPlotStyle(QgsComposition.Print) composition.setPaperSize(paper_width, paper_height) composition.setPrintResolution(300) # Add HTML Frame # noinspection PyCallingNonCallable html_item = QgsComposerHtml(composition, False) margin_left = 10 margin_top = 10 # noinspection PyCallingNonCallable html_frame = QgsComposerFrame( composition, html_item, margin_left, margin_top, paper_width - 2 * margin_left, paper_height - 2 * margin_top) html_item.addFrame(html_frame) # Set HTML # From QGIS 2.6, we can set composer HTML with manual HTML if qgis_version() < 20600: html_path = unique_filename( prefix='report', suffix='.html', dir=temp_dir()) html_to_file(html, file_path=html_path) html_url = QUrl.fromLocalFile(html_path) html_item.setUrl(html_url) else: # noinspection PyUnresolvedReferences html_item.setContentMode(QgsComposerHtml.ManualHtml) # noinspection PyUnresolvedReferences html_item.setResizeMode(QgsComposerHtml.RepeatUntilFinished) html_item.setHtml(html) # RMN: This line below breaks in InaSAFE Headless after one # successful call. This is because the function is not # thread safe. Can't do anything about this, so avoid calling this # function in multithreaded way. html_item.loadHtml() composition.exportAsPDF(output_path) return output_path
def setUp(self): """Run before each test.""" self.iface = get_iface() self.mapSettings = QgsMapSettings() self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) # A4 landscape
def composer(self, setData, flag=True): paperWidth = setData["Width"] paperHeight = setData["Height"] mapRenderer = define._canvas.mapRenderer() # comp = None comp = QgsComposition(mapRenderer) comp.setPlotStyle(QgsComposition.Print) comp.setPaperSize(paperWidth, paperHeight) # rectangleAll = QgsComposerShape(comp) # rectangleAll.setItemPosition(3, 3, paperWidth - 3, paperHeight - 3) # rectangleAll.setShapeType(1) # comp.addItem(rectangleAll) self.txt1 = QgsComposerLabel(comp) self.txt1.setItemPosition(20, 20) self.txt1.setFrameEnabled(False) font = QFont("Arial", 10) self.txt1.setFont(font) self.txt1.setText(setData["TextData"][0]) self.txt1.adjustSizeToText() comp.addItem(self.txt1) self.txt2 = QgsComposerLabel(comp) self.txt2.setItemPosition(int(paperWidth / 2) + 10, 20) self.txt2.setFrameEnabled(False) font = QFont("Arial", 10) self.txt2.setFont(font) self.txt2.setText(setData["TextData"][1]) self.txt2.adjustSizeToText() comp.addItem(self.txt2) self.txt3 = QgsComposerLabel(comp) self.txt3.setItemPosition(paperWidth - 40, 20) self.txt3.setFrameEnabled(False) font = QFont("Arial", 10) self.txt3.setFont(font) self.txt3.setText(setData["TextData"][2]) self.txt3.adjustSizeToText() comp.addItem(self.txt3) w = int((paperWidth - 40) / 4) self.txt4 = QgsComposerLabel(comp) self.txt4.setItemPosition(20, 35) self.txt4.setFrameEnabled(False) font = QFont("Arial", 10) self.txt4.setFont(font) self.txt4.setText(setData["TextData"][3]) self.txt4.adjustSizeToText() comp.addItem(self.txt4) self.txt5 = QgsComposerLabel(comp) self.txt5.setItemPosition(20 + w, 35) self.txt5.setFrameEnabled(False) font = QFont("Arial", 10) self.txt5.setFont(font) self.txt5.setText(setData["TextData"][4]) self.txt5.adjustSizeToText() comp.addItem(self.txt5) self.txt6 = QgsComposerLabel(comp) self.txt6.setItemPosition(20 + w * 2, 35) self.txt6.setFrameEnabled(False) font = QFont("Arial", 10) self.txt6.setFont(font) self.txt6.setText(setData["TextData"][5]) self.txt6.adjustSizeToText() comp.addItem(self.txt6) self.txt7 = QgsComposerLabel(comp) self.txt7.setItemPosition(20 + w * 3, 35) self.txt7.setFrameEnabled(False) font = QFont("Arial", 10) self.txt7.setFont(font) self.txt7.setText(setData["TextData"][6]) self.txt7.adjustSizeToText() comp.addItem(self.txt7) # if flag: x, y = 20, 45 composerMap = QgsComposerMap(comp, x, y, paperWidth - 40, 150) # composerMap.setPreviewMode(QgsComposerMap.Render) # composerMap.setGridEnabled(False) #rect = composerMap.currentMapExtent () renderer = composerMap.mapRenderer() newExtent = renderer.extent() #Make sure the width/height ratio is the same as in current composer map extent. #This is to keep the map item frame and the page layout fixed currentMapExtent = composerMap.currentMapExtent() currentWidthHeightRatio = currentMapExtent.width( ) / currentMapExtent.height() newWidthHeightRatio = newExtent.width() / newExtent.height() if currentWidthHeightRatio < newWidthHeightRatio: #enlarge height of new extent, ensuring the map center stays the same newHeight = newExtent.width() / currentWidthHeightRatio deltaHeight = newHeight - newExtent.height() newExtent.setYMinimum(newExtent.yMinimum() - deltaHeight / 2) newExtent.setYMaximum(newExtent.yMaximum() + deltaHeight / 2) else: #enlarge width of new extent, ensuring the map center stays the same newWidth = currentWidthHeightRatio * newExtent.height() deltaWidth = newWidth - newExtent.width() newExtent.setXMinimum(newExtent.xMinimum() - deltaWidth / 2) newExtent.setXMaximum(newExtent.xMaximum() + deltaWidth / 2) composerMap.beginCommand("Map extent changed") composerMap.setNewExtent(newExtent) composerMap.endCommand() # composerMap.setNewScale(3500) composerMap.setFrameEnabled(True) composerMap.setFrameOutlineWidth(1.0) composerMap.setPreviewMode(QgsComposerMap.Render) composerMap.updateCachedImage() composerMap.setGridEnabled(True) composerMap.setGridPenColor(QColor(255, 255, 255, 0)) composerMap.setGridPenWidth(0.0) #composerMap.setGridStyle(QgsComposerMap.FrameAnnotationsOnly) composerMap.setGridIntervalX(1.0) composerMap.setGridIntervalY(1.0) # mySymbol1 = composerMap.gridLineSymbol () # mySymbol1.setAlpha(0) #composerMap.setGridLineSymbol(mySymbol1) composerMap.setShowGridAnnotation(True) composerMap.setGridAnnotationFormat(1) composerMap.setGridAnnotationPrecision(0) composerMap.setGridAnnotationDirection(1, 0) composerMap.setGridAnnotationDirection(1, 1) comp.addItem(composerMap) w = int((paperWidth - 40) / 3) self.txt8 = QgsComposerLabel(comp) self.txt8.setItemPosition(20, 225) self.txt8.setFrameEnabled(False) font = QFont("Arial", 10) self.txt8.setFont(font) self.txt8.setText(setData["TextData"][7]) self.txt8.adjustSizeToText() comp.addItem(self.txt8) self.txt9 = QgsComposerLabel(comp) self.txt9.setItemPosition(20 + w, 225) self.txt9.setFrameEnabled(False) font = QFont("Arial", 10) self.txt9.setFont(font) self.txt9.setText(setData["TextData"][8]) self.txt9.adjustSizeToText() comp.addItem(self.txt9) self.txt10 = QgsComposerLabel(comp) self.txt10.setItemPosition(20 + w * 2, 225) self.txt10.setFrameEnabled(False) font = QFont("Arial", 10) self.txt10.setFont(font) self.txt10.setText(setData["TextData"][9]) self.txt10.adjustSizeToText() comp.addItem(self.txt10) # profileRect = QgsComposerPicture(comp) # profileRect.setItemPosition(20, 200, paperWidth - 40, 50) # comp.addItem(profileRect) self.txt11 = QgsComposerLabel(comp) self.txt11.setItemPosition(20, paperHeight - 30) self.txt11.setFrameEnabled(False) font = QFont("Arial", 10) self.txt11.setFont(font) self.txt11.setText(setData["TextData"][10]) self.txt11.adjustSizeToText() comp.addItem(self.txt11) self.txt12 = QgsComposerLabel(comp) self.txt12.setItemPosition(20 + w, paperHeight - 30) self.txt12.setFrameEnabled(False) font = QFont("Arial", 10) self.txt12.setFont(font) self.txt12.setText(setData["TextData"][11]) self.txt12.adjustSizeToText() comp.addItem(self.txt12) self.txt13 = QgsComposerLabel(comp) self.txt13.setItemPosition(20 + w * 2, paperHeight - 30) self.txt13.setFrameEnabled(False) font = QFont("Arial", 10) self.txt13.setFont(font) self.txt13.setText(setData["TextData"][12]) self.txt13.adjustSizeToText() comp.addItem(self.txt13) # gpw = QGraphicsProxyWidget() self.tblView = QTableView() tableHeight = None tableWidth = None if self.tableChangedValue == None: self.tblView.setFixedWidth(paperWidth - 40) tableHeight = 50 tableWidth = paperWidth - 40 self.tblView.setFixedHeight(tableHeight) else: self.tblView.setFixedWidth(self.tableChangedValue["Width"]) self.tblView.setFixedHeight(self.tableChangedValue["Height"]) tableHeight = self.tableChangedValue["Height"] tableWidth = self.tableChangedValue["Width"] self.tblView.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.tblView.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) hHeder = self.tblView.horizontalHeader() hHeder.setVisible(False) vHeder = self.tblView.verticalHeader() vHeder.setVisible(False) # if flag: self.stdModel = QStandardItemModel() self.data.__setitem__("TableWidth", tableWidth) self.data.__setitem__("TableHeight", tableHeight) self.setTableView(self.tblView, self.stdModel, self.data) self.calcALT() self.calcTimeRate() self.gpw = QGraphicsProxyWidget() self.gpw.setWidget(self.tblView) # gpw.setWidget(tblView) if self.tableChangedValue == None: self.gpw.setPos(20, 210) font = QFont() font.setPixelSize(2) self.gpw.setFont(font) else: self.gpw.setPos(self.tableChangedValue["X"], self.tableChangedValue["Y"]) font = QFont() font.setPixelSize(self.tableChangedValue["FontSize"]) self.gpw.setFont(font) # tblView.setFrameRect(QRect(20, 210, paperWidth - 40, 50)) comp.addItem(self.gpw) self.connect(self.stdModel, SIGNAL("itemChanged(QStandardItem *)"), self.stdModel_itemChanged) return comp
def setUp(self): """Run before each test.""" self.mComposition = QgsComposition(None) self.mComposition.setPaperSize(297, 210) #A4 landscape
def print_atlas(self, project_path, composer_name, predefined_scales, feature_filter=None, page_name_expression=None): if not feature_filter: QgsMessageLog.logMessage( "atlasprint: NO feature_filter provided !") return None # Get composer from project # in QGIS 2, canno get composers without iface # so we reading project xml and extract composer # in QGIS 3.0, we will use project layoutManager() from xml.etree import ElementTree as ET composer_xml = None with open(project_path, 'r') as f: tree = ET.parse(f) for elem in tree.findall('.//Composer[@title="%s"]' % composer_name): composer_xml = ET.tostring(elem, encoding='utf8', method='xml') if not composer_xml: QgsMessageLog.logMessage("atlasprint: Composer XML not parsed !") return None document = QDomDocument() document.setContent(composer_xml) # Get canvas, map setting & instantiate composition canvas = QgsMapCanvas() QgsProject.instance().read(QFileInfo(project_path)) bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), canvas) bridge.setCanvasLayers() ms = canvas.mapSettings() composition = QgsComposition(ms) # Load content from XML substitution_map = {} composition.loadFromTemplate(document, substitution_map) # Get atlas for this composition atlas = composition.atlasComposition() atlas.setEnabled(True) atlas_map = composition.getComposerMapById(0) atlas_map.setAtlasScalingMode(QgsComposerMap.Predefined) # get project scales atlas.setPredefinedScales(predefined_scales) atlas_map.setAtlasDriven(True) #atlas.setComposerMap(atlas_map) if page_name_expression: atlas.setPageNameExpression(page_name_expression) # Filter feature here to avoid QGIS looping through every feature when doing : composition.setAtlasMode(QgsComposition.ExportAtlas) coverageLayer = atlas.coverageLayer() # Filter by FID as QGIS cannot compile expressions with $id or other $ vars # which leads to bad perfs for big datasets useFid = None if '$id' in feature_filter: import re ids = map(int, re.findall(r'\d+', feature_filter)) if len(ids) > 0: useFid = ids[0] if useFid: qReq = QgsFeatureRequest().setFilterFid(useFid) else: qReq = QgsFeatureRequest().setFilterExpression(feature_filter) # Change feature_filter in order to improve perfs pks = coverageLayer.dataProvider().pkAttributeIndexes() if useFid and len(pks) == 1: pk = coverageLayer.dataProvider().fields()[pks[0]].name() feature_filter = '"%s" IN (%s)' % (pk, useFid) QgsMessageLog.logMessage( "atlasprint: feature_filter changed into: %s" % feature_filter) qReq = QgsFeatureRequest().setFilterExpression(feature_filter) atlas.setFilterFeatures(True) atlas.setFeatureFilter(feature_filter) uid = uuid4() i = 0 # Set Atlas mode composition.setAtlasMode(QgsComposition.ExportAtlas) atlas.beginRender() for feat in coverageLayer.getFeatures(qReq): atlas.prepareForFeature(feat) export_path = os.path.join( tempfile.gettempdir(), '%s_%s.pdf' % (atlas.nameForPage(i), uid)) exported = composition.exportAsPDF(export_path) if not exported or not os.path.isfile(export_path): QgsMessageLog.logMessage( "atlasprint: An error occured while exporting the atlas !") return None break atlas.endRender() if os.path.isfile(export_path): QgsMessageLog.logMessage("atlasprint: path generated %s" % export_path) return export_path