class TestQgsComposerShapes(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerShape = QgsComposerShape(20, 20, 150, 100, self.mComposition) self.mComposerShape.setBackgroundColor(QColor.fromRgb(255, 150, 0)) self.mComposition.addComposerShape(self.mComposerShape) def testRectangle(self): """Test rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) checker = QgsCompositionChecker('composershapes_rectangle', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testEllipse(self): """Test ellipse composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Ellipse) checker = QgsCompositionChecker('composershapes_ellipse', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testTriangle(self): """Test triangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Triangle) checker = QgsCompositionChecker('composershapes_triangle', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testRoundedRectangle(self): """Test rounded rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) self.mComposerShape.setCornerRadius(30) checker = QgsCompositionChecker('composershapes_roundedrect', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() self.mComposerShape.setCornerRadius(0) assert myTestResult, myMessage
class TestQgsComposerPicture(unittest.TestCase): @classmethod def setUpClass(cls): # Bring up a simple HTTP server, for remote picture tests os.chdir(unitTestDataPath() + "") handler = SimpleHTTPServer.SimpleHTTPRequestHandler cls.httpd = SocketServer.TCPServer(("localhost", 0), handler) cls.port = cls.httpd.server_address[1] cls.httpd_thread = threading.Thread(target=cls.httpd.serve_forever) cls.httpd_thread.setDaemon(True) cls.httpd_thread.start() def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) TEST_DATA_DIR = unitTestDataPath() self.pngImage = TEST_DATA_DIR + "/sample_image.png" # create composition self.mapSettings = QgsMapSettings() self.composition = QgsComposition(self.mapSettings) self.composition.setPaperSize(297, 210) self.composerPicture = QgsComposerPicture(self.composition) self.composerPicture.setPicturePath(self.pngImage) self.composerPicture.setSceneRect(QRectF(70, 70, 100, 100)) self.composerPicture.setFrameEnabled(True) self.composition.addComposerPicture(self.composerPicture) def testResizeZoom(self): """Test picture resize zoom mode.""" self.composerPicture.setResizeMode(QgsComposerPicture.Zoom) checker = QgsCompositionChecker("composerpicture_resize_zoom", self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() assert testResult, message def testRemoteImage(self): """Test fetching remote picture.""" self.composerPicture.setPicturePath( "http://localhost:" + str(TestQgsComposerPicture.port) + "/qgis_local_server/logo.png" ) checker = QgsCompositionChecker("composerpicture_remote", self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() self.composerPicture.setPicturePath(self.pngImage) assert testResult, message
class TestQgsComposerEffects(TestCase): def __init__(self, methodName): """Run once on class initialisation.""" unittest.TestCase.__init__(self, methodName) # create composition self.mMapRenderer = QgsMapRenderer() self.mComposition = QgsComposition(self.mMapRenderer) self.mComposition.setPaperSize(297, 210) self.mComposerRect1 = QgsComposerShape(20, 20, 150, 100, self.mComposition) self.mComposerRect1.setShapeType(QgsComposerShape.Rectangle) self.mComposerRect1.setBackgroundColor(QColor.fromRgb(255, 150, 0)) self.mComposition.addComposerShape(self.mComposerRect1) self.mComposerRect2 = QgsComposerShape(50, 50, 150, 100, self.mComposition) self.mComposerRect2.setShapeType(QgsComposerShape.Rectangle) self.mComposerRect2.setBackgroundColor(QColor.fromRgb(0, 100, 150)) self.mComposition.addComposerShape(self.mComposerRect2) def testBlendModes(self): """Test that blend modes work for composer items.""" self.mComposerRect2.setBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composereffects', 'composereffect_blend.png') myTestResult, myMessage = checker.testComposition('Composer effects blending', self.mComposition, myPath) self.mComposerRect2.setBlendMode(QPainter.CompositionMode_SourceOver) assert myTestResult == True, myMessage def testTransparency(self): """Test that transparency works for composer items.""" self.mComposerRect2.setTransparency( 50 ) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composereffects', 'composereffect_transparency.png') myTestResult, myMessage = checker.testComposition('Composer effects transparency', self.mComposition, myPath) self.mComposerRect2.setTransparency( 100 ) assert myTestResult == True, myMessage
class TestQgsComposerEffects(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition self.mMapSettings = QgsMapSettings() self.mComposition = QgsComposition(self.mMapSettings) self.mComposition.setPaperSize(297, 210) self.mComposerRect1 = QgsComposerShape(20, 20, 150, 100, self.mComposition) self.mComposerRect1.setShapeType(QgsComposerShape.Rectangle) self.mComposerRect1.setBackgroundColor(QColor.fromRgb(255, 150, 0)) self.mComposition.addComposerShape(self.mComposerRect1) self.mComposerRect2 = QgsComposerShape(50, 50, 150, 100, self.mComposition) self.mComposerRect2.setShapeType(QgsComposerShape.Rectangle) self.mComposerRect2.setBackgroundColor(QColor.fromRgb(0, 100, 150)) self.mComposition.addComposerShape(self.mComposerRect2) def testBlendModes(self): """Test that blend modes work for composer items.""" self.mComposerRect2.setBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker('composereffects_blend', self.mComposition) checker.setControlPathPrefix("composer_effects") myTestResult, myMessage = checker.testComposition() self.mComposerRect2.setBlendMode(QPainter.CompositionMode_SourceOver) assert myTestResult, myMessage def testTransparency(self): """Test that transparency works for composer items.""" self.mComposerRect2.setTransparency(50) checker = QgsCompositionChecker('composereffects_transparency', self.mComposition) checker.setControlPathPrefix("composer_effects") myTestResult, myMessage = checker.testComposition() self.mComposerRect2.setTransparency(100) assert myTestResult, myMessage
def rotation_test(self): # We will create a polygon layer with a rotated rectangle. # Then we will make it the object layer for the atlas, # rotate the map and test that the bounding rectangle # is smaller than the bounds without rotation. polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory') poly = QgsFeature(polygonLayer.pendingFields()) points = [(10, 15), (15, 10), (45, 40), (40, 45)] poly.setGeometry(QgsGeometry.fromPolygon([[QgsPointXY(x[0], x[1]) for x in points]])) polygonLayer.dataProvider().addFeatures([poly]) QgsProject.instance().addMapLayer(polygonLayer) # Recreating the composer locally composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) # the atlas map atlasMap = QgsComposerMap(composition, 20, 20, 130, 130) atlasMap.setFrameEnabled(True) atlasMap.setLayers([polygonLayer]) atlasMap.setNewExtent(QgsRectangle(0, 0, 100, 50)) composition.addComposerMap(atlasMap) # the atlas atlas = composition.atlasComposition() atlas.setCoverageLayer(polygonLayer) atlas.setEnabled(True) composition.setAtlasMode(QgsComposition.ExportAtlas) atlasMap.setAtlasDriven(True) atlasMap.setAtlasScalingMode(QgsComposerMap.Auto) atlasMap.setAtlasMargin(0.0) # Testing atlasMap.setMapRotation(0.0) atlas.firstFeature() nonRotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) atlasMap.setMapRotation(45.0) atlas.firstFeature() rotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) assert rotatedExtent.width() < nonRotatedExtent.width() * 0.9 assert rotatedExtent.height() < nonRotatedExtent.height() * 0.9 QgsProject.instance().removeMapLayer(polygonLayer)
def testDataDefinedTitle(self): mapSettings = QgsMapSettings() # NOQA composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) legend = QgsComposerLegend(composition) composition.addComposerLegend(legend) legend.setTitle('original') self.assertEqual(legend.title(), 'original') self.assertEqual(legend.legendSettings().title(), 'original') legend.dataDefinedProperties().setProperty(QgsComposerObject.LegendTitle, QgsProperty.fromExpression("'new'")) legend.refreshDataDefinedProperty() self.assertEqual(legend.title(), 'original') self.assertEqual(legend.legendSettings().title(), 'new')
def testDataDefinedColumnCount(self): mapSettings = QgsMapSettings() # NOQA composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) legend = QgsComposerLegend(composition) composition.addComposerLegend(legend) legend.setColumnCount(2) self.assertEqual(legend.columnCount(), 2) self.assertEqual(legend.legendSettings().columnCount(), 2) legend.dataDefinedProperties().setProperty(QgsComposerObject.LegendColumnCount, QgsProperty.fromExpression("5")) legend.refreshDataDefinedProperty() self.assertEqual(legend.columnCount(), 2) self.assertEqual(legend.legendSettings().columnCount(), 5)
def testDataDefinedBackgroundColor(self): mapSettings = QgsMapSettings() # NOQA composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) item = QgsComposerLabel(composition) composition.addComposerLabel(item) item.setBackgroundColor(QColor(255, 0, 0)) self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) self.assertEqual(item.brush().color().name(), QColor(255, 0, 0).name()) item.dataDefinedProperties().setProperty(QgsComposerObject.BackgroundColor, QgsProperty.fromExpression("'blue'")) item.refreshDataDefinedProperty() self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) # should not change self.assertEqual(item.brush().color().name(), QColor(0, 0, 255).name())
def testMapCrs(self): # create composition with composer map map_settings = QgsMapSettings() map_settings.setLayers([self.vector_layer]) composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) # check that new maps inherit project CRS QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) map = QgsComposerMap(composition, 20, 20, 200, 100) map.setFrameEnabled(True) rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909) map.setNewExtent(rectangle) map.setLayers([self.vector_layer]) composition.addComposerMap(map) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) self.assertEqual(map.crs().authid(), 'EPSG:3857') self.assertEqual(map.presetCrs().authid(), 'EPSG:3857') checker = QgsCompositionChecker('composermap_crs3857', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) self.assertEqual(map.presetCrs().authid(), 'EPSG:4326') self.assertEqual(map.crs().authid(), 'EPSG:4326') rectangle = QgsRectangle(-124, 17, -78, 52) map.zoomToExtent(rectangle) checker = QgsCompositionChecker('composermap_crs4326', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # change back to project CRS map.setCrs(QgsCoordinateReferenceSystem()) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid())
def testCase(self): TEST_DATA_DIR = unitTestDataPath() vectorFileInfo = QFileInfo(TEST_DATA_DIR + "/france_parts.shp") mVectorLayer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr") QgsProject.instance().addMapLayers([mVectorLayer]) # create composition with composer map mapSettings = QgsMapSettings() mapSettings.setLayers([mVectorLayer]) mComposition = QgsComposition(QgsProject.instance()) 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 testResizeDisabledCrop(self): """Test that if legend resizing is disabled, and legend is too small, then content is cropped""" 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(s) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) composition.addComposerMap(composer_map) composer_map.setNewExtent(point_layer.extent()) legend = QgsComposerLegend(composition) legend.setSceneRect(QRectF(120, 20, 20, 20)) legend.setFrameEnabled(True) legend.setFrameOutlineWidth(2) legend.setBackgroundColor(QColor(200, 200, 200)) legend.setTitle('') legend.setLegendFilterByMapEnabled(True) # disable auto resizing legend.setResizeToContents(False) composition.addComposerLegend(legend) legend.setComposerMap(composer_map) composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) checker = QgsCompositionChecker( 'composer_legend_noresize_crop', composition) checker.setControlPathPrefix("composer_legend") result, message = checker.testComposition() self.assertTrue(result, message) QgsProject.instance().removeMapLayers([point_layer.id()])
def testResizeDisabled(self): """Test that test legend does not resize if auto size is disabled""" point_path = os.path.join(TEST_DATA_DIR, "points.shp") point_layer = QgsVectorLayer(point_path, "points", "ogr") QgsMapLayerRegistry.instance().addMapLayers([point_layer]) s = QgsMapSettings() s.setLayers([point_layer.id()]) s.setCrsTransformEnabled(False) composition = QgsComposition(s) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) 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) # disable auto resizing legend.setResizeToContents(False) composition.addComposerLegend(legend) legend.setComposerMap(composer_map) composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30)) checker = QgsCompositionChecker("composer_legend_noresize", composition) checker.setControlPathPrefix("composer_legend") result, message = checker.testComposition() self.assertTrue(result, message) QgsMapLayerRegistry.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]) s.setCrsTransformEnabled(False) composition = QgsComposition(s, QgsProject.instance()) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) 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("") 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 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') QgsMapLayerRegistry.instance().addMapLayers([point_layer]) marker_symbol = QgsMarkerSymbolV2.createSimple({'color': '#ff0000', 'outline_style': 'no', 'size': '5', 'size_unit': 'MapUnit'}) point_layer.setRendererV2(QgsSingleSymbolRendererV2(marker_symbol)) s = QgsMapSettings() s.setLayers([point_layer.id()]) s.setCrsTransformEnabled(False) composition = QgsComposition(s) composition.setPaperSize(297, 210) composer_map = QgsComposerMap(composition, 20, 20, 80, 80) composer_map.setFrameEnabled(True) 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('') 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) QgsMapLayerRegistry.instance().removeMapLayers([point_layer.id()])
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 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 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]) 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('') 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 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
class TestQgsComposerHtml(TestCase): def setUp(self): """Run before each test.""" self.mapSettings = QgsMapSettings() self.mComposition = QgsComposition(self.mapSettings) self.mComposition.setPaperSize(297, 210) # A4 landscape def tearDown(self): """Run after each test.""" print "Tear down" def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "test_html.html") myUrl = QUrl("file:///" + myPath) return myUrl def testTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker('composerhtml_table', self.mComposition) checker.setControlPathPrefix("composer_html") myTestResult, myMessage = checker.testComposition() qDebug(myMessage) self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(False) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print "Checking page 1" myPage = 0 checker1 = QgsCompositionChecker('composerhtml_multiframe1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage) assert myTestResult, myMessage print "Checking page 2" myPage = 1 checker2 = QgsCompositionChecker('composerhtml_multiframe2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testHtmlSmartBreaks(self): """Test rendering to multiframes with smart breaks.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 52) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(True) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print "Checking page 1" myPage = 0 checker1 = QgsCompositionChecker('composerhtml_smartbreaks1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage, 200) assert myTestResult, myMessage print "Checking page 2" myPage = 1 checker2 = QgsCompositionChecker('composerhtml_smartbreaks2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage, 200) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testComposerHtmlAccessor(self): """Test that we can retrieve the ComposerHtml instance given an item. """ myComposition = QgsComposition(CANVAS.mapRenderer()) mySubstitutionMap = {'replace-me': 'Foo bar'} myFile = os.path.join(TEST_DATA_DIR, 'template.qpt') myTemplateFile = file(myFile, 'rt') myTemplateContent = myTemplateFile.read() myTemplateFile.close() myDocument = QDomDocument() myDocument.setContent(myTemplateContent) myComposition.loadFromTemplate(myDocument, mySubstitutionMap) myItem = myComposition.getComposerItemById('html-test') myComposerHtml = myComposition.getComposerHtmlByItem(myItem) myMessage = 'Could not retrieve the composer html given an item' assert myComposerHtml is not None, myMessage
class TestQgsComposerMap(unittest.TestCase): 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) self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 1, 2, 3) self.raster_layer.setRenderer(rasterRenderer) myPath = os.path.join(TEST_DATA_DIR, 'points.shp') vector_file_info = QFileInfo(myPath) self.vector_layer = QgsVectorLayer(vector_file_info.filePath(), vector_file_info.completeBaseName(), 'ogr') assert self.vector_layer.isValid() # pipe = mRasterLayer.pipe() # assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsProject.instance().addMapLayers([self.raster_layer, self.vector_layer]) # create composition with composer map self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposerMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(self.mComposerMap) def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker('composermap_overview', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker('composermap_overview_blending', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setInverted(True) checker = QgsCompositionChecker('composermap_overview_invert', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapCenter(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(192, -288, 320, -224) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setInverted(False) overviewMap.overview().setCentered(True) checker = QgsCompositionChecker('composermap_overview_center', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testMapCrs(self): # create composition with composer map map_settings = QgsMapSettings() map_settings.setLayers([self.vector_layer]) composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) # check that new maps inherit project CRS QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) map = QgsComposerMap(composition, 20, 20, 200, 100) map.setFrameEnabled(True) rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909) map.setNewExtent(rectangle) map.setLayers([self.vector_layer]) composition.addComposerMap(map) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) self.assertEqual(map.crs().authid(), 'EPSG:3857') self.assertEqual(map.presetCrs().authid(), 'EPSG:3857') checker = QgsCompositionChecker('composermap_crs3857', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) self.assertEqual(map.presetCrs().authid(), 'EPSG:4326') self.assertEqual(map.crs().authid(), 'EPSG:4326') rectangle = QgsRectangle(-124, 17, -78, 52) map.zoomToExtent(rectangle) checker = QgsCompositionChecker('composermap_crs4326', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # change back to project CRS map.setCrs(QgsCoordinateReferenceSystem()) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXml(documentElement, doc) self.mComposition.addItemsFromXml(documentElement, doc) # test if both composer maps have different ids newMap = QgsComposerMap(self.mComposition, 0, 0, 10, 10) mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testWorldFileGeneration(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setMapRotation(30.0) self.mComposition.setGenerateWorldFile(True) self.mComposition.setReferenceMap(self.mComposerMap) p = self.mComposition.computeWorldFileParameters() pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, 2.4136013686911886, -4.179969388427311, 3342408.5663611) ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) for i in range(0, 6): assert abs(p[i] - pexpected[i]) < ptolerance[i]
class TestQgsComposerMap(TestCase): def __init__(self, methodName): """Run once on class initialisation.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') rasterFileInfo = QFileInfo(myPath) mRasterLayer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer( mRasterLayer.dataProvider(), 2, 3, 4) mRasterLayer.setRenderer(rasterRenderer) #pipe = mRasterLayer.pipe() #assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsMapLayerRegistry.instance().addMapLayers([mRasterLayer]) # create composition with composer map self.mMapRenderer = QgsMapRenderer() layerStringList = QStringList() layerStringList.append(mRasterLayer.id()) self.mMapRenderer.setLayerSet(layerStringList) self.mMapRenderer.setProjectionsEnabled(False) self.mComposition = QgsComposition(self.mMapRenderer) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mComposerMap) def testGrid(self): """Test that we can create a grid for a map.""" myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) self.mComposerMap.setShowGridAnnotation(True) self.mComposerMap.setGridPenWidth(0.5) self.mComposerMap.setGridPenColor(QColor(0,255,0)) self.mComposerMap.setGridAnnotationPrecision(0) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Left) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Top) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Bottom) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Bottom) self.mComposerMap.setAnnotationFontColor(QColor(255,0,0,150)) self.mComposerMap.setGridBlendMode(QPainter.CompositionMode_Overlay) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_grid.png') myTestResult, myMessage = checker.testComposition('Composer map grid', self.mComposition, myPath) self.mComposerMap.setGridEnabled(False) self.mComposerMap.setShowGridAnnotation(False) assert myTestResult == True, myMessage def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview_blend.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview blending', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(True) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview_invert.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview inverted', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage # Fails because addItemsFromXML has been commented out in sip @expectedFailure def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXML(documentElement, doc) self.mComposition.addItemsFromXML(documentElement, doc, 0, False) #test if both composer maps have different ids newMap = QgsComposerMap() mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testZebraStyle(self): self.mComposerMap.setGridFrameStyle(QgsComposerMap.Zebra) myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent( myRectangle ) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_zebra_style.png') testResult, myMessage = checker.testComposition('Composer map zebra', self.mComposition, myPngPath) assert testResult == True, myMessage
class TestQgsComposerMap(unittest.TestCase): def __init__(self, methodName): """Run once on class initialisation.""" 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.mMapRenderer = QgsMapRenderer() layerStringList = [] layerStringList.append(mRasterLayer.id()) self.mMapRenderer.setLayerSet(layerStringList) self.mMapRenderer.setProjectionsEnabled(False) self.mComposition = QgsComposition(self.mMapRenderer) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mComposerMap) def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker('composermap_overview', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker('composermap_overview_blending', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(True) checker = QgsCompositionChecker('composermap_overview_invert', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapCenter(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(192, -288, 320, -224) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(False) overviewMap.setOverviewCentered(True) checker = QgsCompositionChecker('composermap_overview_center', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage # Fails because addItemsFromXML has been commented out in sip @unittest.expectedFailure def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXML(documentElement, doc) self.mComposition.addItemsFromXML(documentElement, doc, 0, False) # test if both composer maps have different ids newMap = QgsComposerMap() mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testWorldFileGeneration(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setMapRotation(30.0) self.mComposition.setGenerateWorldFile(True) self.mComposition.setWorldFileMap(self.mComposerMap) p = self.mComposition.computeWorldFileParameters() pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, 2.4136013686911886, -4.179969388427311, 3342408.5663611) ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) for i in range(0, 6): assert abs(p[i] - pexpected[i]) < ptolerance[i]
class TestQgsAtlasComposition(unittest.TestCase): 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 filename_test(self): self.mAtlas.setFilenamePattern("'output_' || $feature") self.mAtlas.beginRender() for i in range(0, self.mAtlas.numFeatures()): self.mAtlas.prepareForFeature(i) expected = "output_%d" % (i + 1) assert self.mAtlas.currentFilename() == expected self.mAtlas.endRender() def autoscale_render_test(self): self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) self.mAtlasMap.setAtlasMargin(0.10) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlasMap.setAtlasDriven(False) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlasMap.setAtlasMargin(0) def autoscale_render_test_old_api(self): self.mAtlas.setComposerMap(self.mAtlasMap) self.mAtlas.setFixedScale(False) self.mAtlas.setMargin(0.10) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale_old_api%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlas.setFixedScale(True) self.mAtlas.setMargin(0) self.mAtlas.setComposerMap(None) self.mAtlasMap.setAtlasDriven(False) def fixedscale_render_test(self): self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_fixedscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def predefinedscales_render_test(self): self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Predefined) scales = [1800000, 5000000] self.mAtlas.setPredefinedScales(scales) for i, s in enumerate(self.mAtlas.predefinedScales()): assert s == scales[i] self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_predefinedscales%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def hidden_render_test(self): self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(True) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_hiding%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlas.setHideCoverage(False) def sorting_render_test(self): self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(True) self.mAtlas.setSortKeyAttributeIndex(4) # departement name self.mAtlas.setSortAscending(False) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_sorting%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def filtering_render_test(self): self.mAtlasMap.setNewExtent(QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(False) self.mAtlas.setFilterFeatures(True) self.mAtlas.setFeatureFilter("substr(NAME_1,1,1)='P'") # select only 'Pays de la loire' self.mAtlas.beginRender() for i in range(0, 1): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_filtering%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def legend_test(self): self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) self.mAtlasMap.setAtlasMargin(0.10) # add a point layer ptLayer = QgsVectorLayer("Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory") pr = ptLayer.dataProvider() f1 = QgsFeature(1) f1.initAttributes(2) f1.setAttribute(0, 1) f1.setAttribute(1, "Test label 1") f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(-0.638, 48.954))) f2 = QgsFeature(2) f2.initAttributes(2) f2.setAttribute(0, 2) f2.setAttribute(1, "Test label 2") f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(-1.682, 48.550))) pr.addFeatures([f1, f2]) # categorized symbology r = QgsCategorizedSymbolRendererV2("attr", [QgsRendererCategoryV2(1, QgsMarkerSymbolV2.createSimple({"color": "255,0,0"}), "red"), QgsRendererCategoryV2(2, QgsMarkerSymbolV2.createSimple({"color": "0,0,255"}), "blue")]) ptLayer.setRendererV2(r) QgsMapLayerRegistry.instance().addMapLayer(ptLayer) # add the point layer to the map settings layers = self.mapSettings.layers() layers = [ptLayer.id()] + layers self.mapSettings.setLayers(layers) # add a legend legend = QgsComposerLegend(self.mComposition) legend.moveBy(200, 100) # sets the legend filter parameter legend.setComposerMap(self.mAtlasMap) legend.setLegendFilterOutAtlas(True) self.mComposition.addComposerLegend(legend) self.mAtlas.beginRender() self.mAtlas.prepareForFeature(0) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_legend', self.mComposition) myTestResult, myMessage = checker.testComposition() assert myTestResult self.mAtlas.endRender() # restore state self.mapSettings.setLayers([layers[1]]) self.mComposition.removeComposerItem(legend) QgsMapLayerRegistry.instance().removeMapLayer(ptLayer.id())
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
class TestQgsComposerHtml(unittest.TestCase): 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 tearDown(self): """Run after each test.""" print("Tear down") def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "test_html.html") myUrl = QUrl("file:///" + myPath) return myUrl def testTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker('composerhtml_table', self.mComposition) checker.setControlPathPrefix("composer_html") myTestResult, myMessage = checker.testComposition() qDebug(myMessage) self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(False) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print("Checking page 1") myPage = 0 checker1 = QgsCompositionChecker('composerhtml_multiframe1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage) assert myTestResult, myMessage print("Checking page 2") myPage = 1 checker2 = QgsCompositionChecker('composerhtml_multiframe2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testHtmlSmartBreaks(self): """Test rendering to multiframes with smart breaks.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 52) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(True) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print("Checking page 1") myPage = 0 checker1 = QgsCompositionChecker('composerhtml_smartbreaks1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage, 200) assert myTestResult, myMessage print("Checking page 2") myPage = 1 checker2 = QgsCompositionChecker('composerhtml_smartbreaks2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage, 200) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage
class TestQgsComposerPicture(unittest.TestCase): @classmethod def setUpClass(cls): # Bring up a simple HTTP server, for remote picture tests os.chdir(unitTestDataPath() + '') handler = http.server.SimpleHTTPRequestHandler cls.httpd = socketserver.TCPServer(('localhost', 0), handler) cls.port = cls.httpd.server_address[1] cls.httpd_thread = threading.Thread(target=cls.httpd.serve_forever) cls.httpd_thread.setDaemon(True) cls.httpd_thread.start() def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) TEST_DATA_DIR = unitTestDataPath() self.pngImage = TEST_DATA_DIR + "/sample_image.png" # create composition self.composition = QgsComposition(QgsProject.instance()) self.composition.setPaperSize(297, 210) self.composerPicture = QgsComposerPicture(self.composition) self.composerPicture.setPicturePath(self.pngImage) self.composerPicture.setSceneRect(QRectF(70, 70, 100, 100)) self.composerPicture.setFrameEnabled(True) self.composition.addComposerPicture(self.composerPicture) def testResizeZoom(self): """Test picture resize zoom mode.""" self.composerPicture.setResizeMode(QgsComposerPicture.Zoom) checker = QgsCompositionChecker('composerpicture_resize_zoom', self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() assert testResult, message @unittest.skip('test is broken for qt5/python3 - feature works') def testRemoteImage(self): """Test fetching remote picture.""" self.composerPicture.setPicturePath('http://localhost:' + str(TestQgsComposerPicture.port) + '/qgis_local_server/logo.png') checker = QgsCompositionChecker('composerpicture_remote', self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() self.composerPicture.setPicturePath(self.pngImage) assert testResult, message def testGridNorth(self): """Test syncing picture to grid north""" composition = QgsComposition(QgsProject.instance()) 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 testTrueNorth(self): """Test syncing picture to true north""" composition = QgsComposition(QgsProject.instance()) composerMap = QgsComposerMap(composition) composerMap.setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575)) 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)
class TestQgsComposerMap(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition with composer map self.mMapSettings = QgsMapSettings() crs = QgsCoordinateReferenceSystem(32633) self.mMapSettings.setDestinationCrs(crs) self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposerMap.setBackgroundColor(QColor(150, 100, 100)) self.mComposition.addComposerMap(self.mComposerMap) def testGrid(self): """Test that we can create a grid for a map.""" myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(True) self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setAnnotationFont( QgsFontUtils.getStandardTestFont()) self.mComposerMap.grid().setAnnotationPrecision(0) self.mComposerMap.grid().setAnnotationDisplay( QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Left) self.mComposerMap.grid().setAnnotationPosition( QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Right) self.mComposerMap.grid().setAnnotationDisplay( QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Top) self.mComposerMap.grid().setAnnotationPosition( QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Bottom) self.mComposerMap.grid().setAnnotationDirection( QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Right) self.mComposerMap.grid().setAnnotationDirection( QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Bottom) self.mComposerMap.grid().setAnnotationFontColor(QColor(255, 0, 0, 150)) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_Overlay) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_grid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testCrossGrid(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Cross) self.mComposerMap.grid().setCrossLength(2.0) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_crossgrid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testMarkerGrid(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Markers) self.mComposerMap.grid().setCrossLength(2.0) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_markergrid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testFrameOnly(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle( QgsComposerMapGrid.FrameAnnotationsOnly) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFramePenSize(0.5) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_gridframeonly', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) assert myTestResult, myMessage def testZebraStyle(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setFramePenColor(QColor(255, 100, 0, 200)) self.mComposerMap.grid().setFrameFillColor1(QColor(50, 90, 50, 100)) self.mComposerMap.grid().setFrameFillColor2(QColor(200, 220, 100, 60)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage def testZebraStyleSides(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) self.mComposerMap.grid().setFrameFillColor1(QColor(0, 0, 0)) self.mComposerMap.grid().setFrameFillColor2(QColor(255, 255, 255)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameLeft, True) self.mComposerMap.grid().setFrameSideFlag( QgsComposerMapGrid.FrameRight, False) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, False) self.mComposerMap.grid().setFrameSideFlag( QgsComposerMapGrid.FrameBottom, False) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_left', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_lefttop', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag( QgsComposerMapGrid.FrameRight, True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_lefttopright', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag( QgsComposerMapGrid.FrameBottom, True) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) def testInteriorTicks(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode( QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle( QgsComposerMapGrid.InteriorTicks) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle( QgsComposerMapGrid.FrameAnnotationsOnly) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_interiorticks', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage
class TestQgsAtlasComposition(unittest.TestCase): 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 mMapRenderer = QgsMapRenderer() layerStringList = [] layerStringList.append(mVectorLayer.id()) mMapRenderer.setLayerSet(layerStringList) mMapRenderer.setProjectionsEnabled(True) mMapRenderer.setMapUnits(QGis.Meters) # select epsg:2154 crs = QgsCoordinateReferenceSystem() crs.createFromSrid(2154) mMapRenderer.setDestinationCrs(crs) self.mComposition = QgsComposition(mMapRenderer) 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() shutil.rmtree(tmppath, True) def filename_test(self): self.mAtlas.setFilenamePattern("'output_' || $feature") self.mAtlas.beginRender() for i in range(0, self.mAtlas.numFeatures()): self.mAtlas.prepareForFeature(i) expected = "output_%d" % (i + 1) assert self.mAtlas.currentFilename() == expected self.mAtlas.endRender() def autoscale_render_test(self): self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) self.mAtlasMap.setAtlasMargin(0.10) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlasMap.setAtlasDriven(False) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlasMap.setAtlasMargin(0) def autoscale_render_test_old_api(self): self.mAtlas.setComposerMap(self.mAtlasMap) self.mAtlas.setFixedScale(False) self.mAtlas.setMargin(0.10) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker( 'atlas_autoscale_old_api%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlas.setFixedScale(True) self.mAtlas.setMargin(0) self.mAtlas.setComposerMap(None) self.mAtlasMap.setAtlasDriven(False) def fixedscale_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_fixedscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def predefinedscales_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Predefined) scales = [1800000, 5000000] self.mAtlas.setPredefinedScales(scales) for i, s in enumerate(self.mAtlas.predefinedScales()): assert s == scales[i] self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker( 'atlas_predefinedscales%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def hidden_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(True) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_hiding%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def sorting_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(True) self.mAtlas.setSortKeyAttributeIndex(4) # departement name self.mAtlas.setSortAscending(False) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_sorting%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def filtering_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(False) self.mAtlas.setFilterFeatures(True) self.mAtlas.setFeatureFilter( "substr(NAME_1,1,1)='P'") # select only 'Pays de la loire' self.mAtlas.beginRender() for i in range(0, 1): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_filtering%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender()
class TestQgsComposerShapes(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerShape = QgsComposerShape(20, 20, 150, 100, self.mComposition) self.mComposerShape.setBackgroundColor(QColor.fromRgb(255, 150, 0)) self.mComposition.addComposerShape(self.mComposerShape) def testRectangle(self): """Test rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) checker = QgsCompositionChecker('composershapes_rectangle', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testEllipse(self): """Test ellipse composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Ellipse) checker = QgsCompositionChecker('composershapes_ellipse', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testTriangle(self): """Test triangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Triangle) checker = QgsCompositionChecker('composershapes_triangle', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testRoundedRectangle(self): """Test rounded rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) self.mComposerShape.setCornerRadius(30) checker = QgsCompositionChecker('composershapes_roundedrect', self.mComposition) checker.setControlPathPrefix("composer_shapes") myTestResult, myMessage = checker.testComposition() self.mComposerShape.setCornerRadius(0) assert myTestResult, myMessage
class TestQgsComposerHtml(unittest.TestCase): def setUp(self): """Run before each test.""" self.mComposition = QgsComposition(None) self.mComposition.setPaperSize(297, 210) #A4 landscape def tearDown(self): """Run after each test.""" print "Tear down" def controlImagePath(self, theImageName): """Helper to get the path to a control image.""" myPath = os.path.join(TEST_DATA_DIR, "control_images", "expected_composerhtml", theImageName) assert os.path.exists(myPath) return myPath def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "html_table.html") myUrl = QUrl(QString("file:///%1").arg(myPath)) return myUrl @expectedFailure def XtestTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker() myResult, myMessage = checker.testComposition( "Composer html table", self.mComposition, self.controlImagePath("composerhtml_table.png")) qDebug(myMessage) assert myResult, myMessage @expectedFailure def XtestTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) myPage = 0 checker1 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe1.png") print "Checking page 1" myResult, myMessage = checker1.testComposition("Composer html table", self.mComposition, myControlImage, myPage) assert myResult, myMessage myPage = 1 checker2 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe2.png") print "Checking page 2" myResult, myMessage = checker2.testComposition("Composer html table", self.mComposition, myControlImage, myPage) assert myResult, myMessage myPage = 2 checker3 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe3.png") myResult, myMessage = checker3.testComposition("Composer html table", self.mComposition, myControlImage, myPage) print "Checking page 3" assert myResult, myMessage def testComposerHtmlAccessor(self): """Test that we can retrieve the ComposerHtml instance given an item. """ myComposition = QgsComposition(CANVAS.mapRenderer()) mySubstitutionMap = {'replace-me': 'Foo bar'} myFile = os.path.join(TEST_DATA_DIR, 'template.qpt') myTemplateFile = file(myFile, 'rt') myTemplateContent = myTemplateFile.read() myTemplateFile.close() myDocument = QDomDocument() myDocument.setContent(myTemplateContent) myComposition.loadFromTemplate(myDocument, mySubstitutionMap) myItem = myComposition.getComposerItemById('html-test') myComposerHtml = myComposition.getComposerHtmlByItem(myItem) myMessage = 'Could not retrieve the composer html given an item' assert myComposerHtml is not None, myMessage
class Map(): """A class for creating a map.""" def __init__(self, theIface): """Constructor for the Map class. Args: theIface - reference to the QGIS iface object Returns: None Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map class initialised') self.iface = theIface self.layer = theIface.activeLayer() self.keywordIO = KeywordIO() self.printer = None self.composition = None self.legend = None self.pageWidth = 210 # width in mm self.pageHeight = 297 # height in mm self.pageDpi = 300.0 self.pageMargin = 10 # margin in mm self.verticalSpacing = 1 # vertical spacing between elements self.showFramesFlag = False # intended for debugging use only # make a square map where width = height = page width self.mapHeight = self.pageWidth - (self.pageMargin * 2) self.mapWidth = self.mapHeight self.disclaimer = self.tr('InaSAFE has been jointly developed by' ' BNPB, AusAid & the World Bank') def tr(self, theString): """We implement this since we do not inherit QObject. Args: theString - string for translation. Returns: Translated version of theString. Raises: no exceptions explicitly raised. """ return QtCore.QCoreApplication.translate('Map', theString) def setImpactLayer(self, theLayer): """Mutator for the impact layer that will be used for stats, legend and reporting. Args: theLayer - a valid QgsMapLayer Returns: None Raises: Any exceptions raised by the InaSAFE library will be propagated. """ self.layer = theLayer def setupComposition(self): """Set up the composition ready for drawing elements onto it. Args: None Returns: None Raises: None """ LOGGER.debug('InaSAFE Map setupComposition called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() self.composition = QgsComposition(myRenderer) self.composition.setPlotStyle(QgsComposition.Print) # or preview self.composition.setPaperSize(self.pageWidth, self.pageHeight) self.composition.setPrintResolution(self.pageDpi) self.composition.setPrintAsRaster(True) def composeMap(self): """Place all elements on the map ready for printing. Args: None Returns: None Raises: Any exceptions raised will be propagated. """ self.setupComposition() # Keep track of our vertical positioning as we work our way down # the page placing elements on it. myTopOffset = self.pageMargin self.drawLogo(myTopOffset) myLabelHeight = self.drawTitle(myTopOffset) # Update the map offset for the next row of content myTopOffset += myLabelHeight + self.verticalSpacing myComposerMap = self.drawMap(myTopOffset) self.drawScaleBar(myComposerMap, myTopOffset) # Update the top offset for the next horizontal row of items myTopOffset += self.mapHeight + self.verticalSpacing - 1 myImpactTitleHeight = self.drawImpactTitle(myTopOffset) # Update the top offset for the next horizontal row of items if myImpactTitleHeight: myTopOffset += myImpactTitleHeight + self.verticalSpacing + 2 self.drawLegend(myTopOffset) self.drawHostAndTime(myTopOffset) self.drawDisclaimer() def renderComposition(self): """Render the map composition to an image and save that to disk. Args: None Returns: tuple: * str: myImagePath - absolute path to png of rendered map * QImage: myImage - in memory copy of rendered map * QRectF: myTargetArea - dimensions of rendered map str: Absolute file system path to the rendered image. Raises: None """ LOGGER.debug('InaSAFE Map renderComposition called') # NOTE: we ignore self.composition.printAsRaster() and always rasterise myWidth = (int)(self.pageDpi * self.pageWidth / 25.4) myHeight = (int)(self.pageDpi * self.pageHeight / 25.4) myImage = QtGui.QImage(QtCore.QSize(myWidth, myHeight), QtGui.QImage.Format_ARGB32) myImage.setDotsPerMeterX(dpiToMeters(self.pageDpi)) myImage.setDotsPerMeterY(dpiToMeters(self.pageDpi)) # Only works in Qt4.8 #myImage.fill(QtGui.qRgb(255, 255, 255)) # Works in older Qt4 versions myImage.fill(55 + 255 * 256 + 255 * 256 * 256) myImagePainter = QtGui.QPainter(myImage) mySourceArea = QtCore.QRectF(0, 0, self.pageWidth, self.pageHeight) myTargetArea = QtCore.QRectF(0, 0, myWidth, myHeight) self.composition.render(myImagePainter, myTargetArea, mySourceArea) myImagePainter.end() myImagePath = unique_filename(prefix='mapRender_', suffix='.png', dir=temp_dir()) myImage.save(myImagePath) return myImagePath, myImage, myTargetArea def printToPdf(self, theFilename): """Generate the printout for our final map. Args: theFilename: str - optional path on the file system to which the pdf should be saved. If None, a generated file name will be used. Returns: str: file name of the output file (equivalent to theFilename if provided). Raises: None """ LOGGER.debug('InaSAFE Map printToPdf called') if theFilename is None: myMapPdfPath = unique_filename(prefix='report', suffix='.pdf', dir=temp_dir('work')) else: # We need to cast to python string in case we receive a QString myMapPdfPath = str(theFilename) self.composeMap() self.printer = setupPrinter(myMapPdfPath) _, myImage, myRectangle = self.renderComposition() myPainter = QtGui.QPainter(self.printer) myPainter.drawImage(myRectangle, myImage, myRectangle) myPainter.end() return myMapPdfPath def drawLogo(self, theTopOffset): """Add a picture containing the logo to the map top left corner Args: theTopOffset - vertical offset at which the logo shoudl be drawn Returns: None Raises: None """ myLogo = QgsComposerPicture(self.composition) myLogo.setPictureFile(':/plugins/inasafe/bnpb_logo.png') myLogo.setItemPosition(self.pageMargin, theTopOffset, 10, 10) if qgisVersion() >= 10800: # 1.8 or newer myLogo.setFrameEnabled(self.showFramesFlag) else: myLogo.setFrame(self.showFramesFlag) myLogo.setZValue(1) # To ensure it overlays graticule markers self.composition.addItem(myLogo) def drawTitle(self, theTopOffset): """Add a title to the composition. Args: theTopOffset - vertical offset at which the map should be drawn Returns: float - the height of the label as rendered Raises: None """ LOGGER.debug('InaSAFE Map drawTitle called') myFontSize = 14 myFontWeight = QtGui.QFont.Bold myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myHeading = self.tr('InaSAFE - Indonesia Scenario Assessment' ' for Emergencies') myLabel.setText(myHeading) myLabel.adjustSizeToText() myLabelHeight = 10.0 # determined using qgis map composer myLabelWidth = 170.0 # item - position and size...option myLeftOffset = self.pageWidth - self.pageMargin - myLabelWidth myLabel.setItemPosition( myLeftOffset, theTopOffset - 2, # -2 to push it up a little myLabelWidth, myLabelHeight, ) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel) return myLabelHeight def drawMap(self, theTopOffset): """Add a map to the composition and return the compsermap instance. Args: theTopOffset - vertical offset at which the map should be drawn Returns: A QgsComposerMap instance is returned Raises: None """ LOGGER.debug('InaSAFE Map drawMap called') myMapWidth = self.mapWidth myComposerMap = QgsComposerMap(self.composition, self.pageMargin, theTopOffset, myMapWidth, self.mapHeight) #myExtent = self.iface.mapCanvas().extent() # The dimensions of the map canvas and the print compser map may # differ. So we set the map composer extent using the canvas and # then defer to the map canvas's map extents thereafter # Update: disabled as it results in a rectangular rather than # square map #myComposerMap.setNewExtent(myExtent) myComposerExtent = myComposerMap.extent() # Recenter the composer map on the center of the canvas # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge myCanvasExtent = self.iface.mapCanvas().extent() myWidth = myCanvasExtent.width() myHeight = myCanvasExtent.height() myLongestLength = myWidth if myWidth < myHeight: myLongestLength = myHeight myHalfLength = myLongestLength / 2 myCenter = myCanvasExtent.center() myMinX = myCenter.x() - myHalfLength myMaxX = myCenter.x() + myHalfLength myMinY = myCenter.y() - myHalfLength myMaxY = myCenter.y() + myHalfLength mySquareExtent = QgsRectangle(myMinX, myMinY, myMaxX, myMaxY) myComposerMap.setNewExtent(mySquareExtent) myComposerMap.setGridEnabled(True) myNumberOfSplits = 5 # .. todo:: Write logic to adjust preciosn so that adjacent tick marks # always have different displayed values myPrecision = 2 myXInterval = myComposerExtent.width() / myNumberOfSplits myComposerMap.setGridIntervalX(myXInterval) myYInterval = myComposerExtent.height() / myNumberOfSplits myComposerMap.setGridIntervalY(myYInterval) myComposerMap.setGridStyle(QgsComposerMap.Cross) myCrossLengthMM = 1 myComposerMap.setCrossLength(myCrossLengthMM) myComposerMap.setZValue(0) # To ensure it does not overlay logo myFontSize = 6 myFontWeight = QtGui.QFont.Normal myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myComposerMap.setGridAnnotationFont(myFont) myComposerMap.setGridAnnotationPrecision(myPrecision) myComposerMap.setShowGridAnnotation(True) myComposerMap.setGridAnnotationDirection( QgsComposerMap.BoundaryDirection) self.composition.addItem(myComposerMap) self.drawGraticuleMask(theTopOffset) return myComposerMap def drawGraticuleMask(self, theTopOffset): """A helper function to mask out graticule labels on the right side by over painting a white rectangle with white border on them. Args: theTopOffset - vertical offset at which the map should be drawn Returns: None Raises: None """ LOGGER.debug('InaSAFE Map drawGraticuleMask called') myLeftOffset = self.pageMargin + self.mapWidth myRect = QgsComposerShape(myLeftOffset + 0.5, theTopOffset, self.pageWidth - myLeftOffset, self.mapHeight + 1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myRect.setLineWidth(0.1) myRect.setFrame(False) myRect.setOutlineColor(QtGui.QColor(255, 255, 255)) myRect.setFillColor(QtGui.QColor(255, 255, 255)) myRect.setOpacity(100) # These two lines seem superfluous but are needed myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) myRect.setBrush(myBrush) self.composition.addItem(myRect) def drawNativeScaleBar(self, theComposerMap, theTopOffset): """Draw a scale bar using QGIS' native drawing - in the case of geographic maps, scale will be in degrees, not km. Args: None Returns: None Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map drawNativeScaleBar called') myScaleBar = QgsComposerScaleBar(self.composition) myScaleBar.setStyle('Numeric') # optionally modify the style myScaleBar.setComposerMap(theComposerMap) myScaleBar.applyDefaultSize() myScaleBarHeight = myScaleBar.boundingRect().height() myScaleBarWidth = myScaleBar.boundingRect().width() # -1 to avoid overlapping the map border myScaleBar.setItemPosition( self.pageMargin + 1, theTopOffset + self.mapHeight - (myScaleBarHeight * 2), myScaleBarWidth, myScaleBarHeight) myScaleBar.setFrame(self.showFramesFlag) # Disabled for now #self.composition.addItem(myScaleBar) def drawScaleBar(self, theComposerMap, theTopOffset): """Add a numeric scale to the bottom left of the map We draw the scale bar manually because QGIS does not yet support rendering a scalebar for a geographic map in km. .. seealso:: :meth:`drawNativeScaleBar` Args: * theComposerMap - QgsComposerMap instance used as the basis scale calculations. * theTopOffset - vertical offset at which the map should be drawn Returns: None Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map drawScaleBar called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() # # Add a linear map scale # myDistanceArea = QgsDistanceArea() myDistanceArea.setSourceCrs(myRenderer.destinationCrs().srsid()) myDistanceArea.setProjectionsEnabled(True) # Determine how wide our map is in km/m # Starting point at BL corner myComposerExtent = theComposerMap.extent() myStartPoint = QgsPoint(myComposerExtent.xMinimum(), myComposerExtent.yMinimum()) # Ending point at BR corner myEndPoint = QgsPoint(myComposerExtent.xMaximum(), myComposerExtent.yMinimum()) myGroundDistance = myDistanceArea.measureLine(myStartPoint, myEndPoint) # Get the equivalent map distance per page mm myMapWidth = self.mapWidth # How far is 1mm on map on the ground in meters? myMMToGroundDistance = myGroundDistance / myMapWidth #print 'MM:', myMMDistance # How long we want the scale bar to be in relation to the map myScaleBarToMapRatio = 0.5 # How many divisions the scale bar should have myTickCount = 5 myScaleBarWidthMM = myMapWidth * myScaleBarToMapRatio myPrintSegmentWidthMM = myScaleBarWidthMM / myTickCount # Segment width in real world (m) # We apply some logic here so that segments are displayed in meters # if each segment is less that 1000m otherwise km. Also the segment # lengths are rounded down to human looking numbers e.g. 1km not 1.1km myUnits = '' myGroundSegmentWidth = myPrintSegmentWidthMM * myMMToGroundDistance if myGroundSegmentWidth < 1000: myUnits = 'm' myGroundSegmentWidth = round(myGroundSegmentWidth) # adjust the segment width now to account for rounding myPrintSegmentWidthMM = myGroundSegmentWidth / myMMToGroundDistance else: myUnits = 'km' # Segment with in real world (km) myGroundSegmentWidth = round(myGroundSegmentWidth / 1000) myPrintSegmentWidthMM = ((myGroundSegmentWidth * 1000) / myMMToGroundDistance) # Now adjust the scalebar width to account for rounding myScaleBarWidthMM = myTickCount * myPrintSegmentWidthMM #print "SBWMM:", myScaleBarWidthMM #print "SWMM:", myPrintSegmentWidthMM #print "SWM:", myGroundSegmentWidthM #print "SWKM:", myGroundSegmentWidthKM # start drawing in line segments myScaleBarHeight = 5 # mm myLineWidth = 0.3 # mm myInsetDistance = 7 # how much to inset the scalebar into the map by myScaleBarX = self.pageMargin + myInsetDistance myScaleBarY = (theTopOffset + self.mapHeight - myInsetDistance - myScaleBarHeight) # mm # Draw an outer background box - shamelessly hardcoded buffer myRect = QgsComposerShape( myScaleBarX - 4, # left edge myScaleBarY - 3, # top edge myScaleBarWidthMM + 13, # right edge myScaleBarHeight + 6, # bottom edge self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myRect.setLineWidth(myLineWidth) myRect.setFrame(False) myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) # workaround for missing setTransparentFill missing from python api myRect.setBrush(myBrush) self.composition.addItem(myRect) # Set up the tick label font myFontWeight = QtGui.QFont.Normal myFontSize = 6 myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) # Draw the bottom line myUpshift = 0.3 # shift the bottom line up for better rendering myRect = QgsComposerShape(myScaleBarX, myScaleBarY + myScaleBarHeight - myUpshift, myScaleBarWidthMM, 0.1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myRect.setLineWidth(myLineWidth) myRect.setFrame(False) self.composition.addItem(myRect) # Now draw the scalebar ticks for myTickCountIterator in range(0, myTickCount + 1): myDistanceSuffix = '' if myTickCountIterator == myTickCount: myDistanceSuffix = ' ' + myUnits myRealWorldDistance = ( '%.0f%s' % (myTickCountIterator * myGroundSegmentWidth, myDistanceSuffix)) #print 'RW:', myRealWorldDistance myMMOffset = myScaleBarX + (myTickCountIterator * myPrintSegmentWidthMM) #print 'MM:', myMMOffset myTickHeight = myScaleBarHeight / 2 # Lines are not exposed by the api yet so we # bodge drawing lines using rectangles with 1px height or width myTickWidth = 0.1 # width or rectangle to be drawn myUpTickLine = QgsComposerShape( myMMOffset, myScaleBarY + myScaleBarHeight - myTickHeight, myTickWidth, myTickHeight, self.composition) myUpTickLine.setShapeType(QgsComposerShape.Rectangle) myUpTickLine.setLineWidth(myLineWidth) myUpTickLine.setFrame(False) self.composition.addItem(myUpTickLine) # # Add a tick label # myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myRealWorldDistance) myLabel.adjustSizeToText() myLabel.setItemPosition(myMMOffset - 3, myScaleBarY - myTickHeight) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel) def drawImpactTitle(self, theTopOffset): """Draw the map subtitle - obtained from the impact layer keywords. Args: theTopOffset - vertical offset at which to begin drawing Returns: float - the height of the label as rendered Raises: None """ LOGGER.debug('InaSAFE Map drawImpactTitle called') myTitle = self.getMapTitle() if myTitle is None: myTitle = '' myFontSize = 20 myFontWeight = QtGui.QFont.Bold myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myHeading = myTitle myLabel.setText(myHeading) myLabelWidth = self.pageWidth - (self.pageMargin * 2) myLabelHeight = 12 myLabel.setItemPosition(self.pageMargin, theTopOffset, myLabelWidth, myLabelHeight) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel) return myLabelHeight def drawLegend(self, theTopOffset): """Add a legend to the map using our custom legend renderer. .. note:: getLegend generates a pixmap in 150dpi so if you set the map to a higher dpi it will appear undersized. Args: theTopOffset - vertical offset at which to begin drawing Returns: None Raises: None """ LOGGER.debug('InaSAFE Map drawLegend called') mapLegendAttributes = self.getMapLegendAtributes() legendNotes = mapLegendAttributes.get('legend_notes', None) legendUnits = mapLegendAttributes.get('legend_units', None) legendTitle = mapLegendAttributes.get('legend_title', None) LOGGER.debug(mapLegendAttributes) myLegend = MapLegend(self.layer, self.pageDpi, legendTitle, legendNotes, legendUnits) self.legend = myLegend.getLegend() myPicture1 = QgsComposerPicture(self.composition) myLegendFilePath = unique_filename(prefix='legend', suffix='.png', dir='work') self.legend.save(myLegendFilePath, 'PNG') myPicture1.setPictureFile(myLegendFilePath) myLegendHeight = pointsToMM(self.legend.height(), self.pageDpi) myLegendWidth = pointsToMM(self.legend.width(), self.pageDpi) myPicture1.setItemPosition(self.pageMargin, theTopOffset, myLegendWidth, myLegendHeight) myPicture1.setFrame(False) self.composition.addItem(myPicture1) os.remove(myLegendFilePath) def drawImage(self, theImage, theWidthMM, theLeftOffset, theTopOffset): """Helper to draw an image directly onto the QGraphicsScene. This is an alternative to using QgsComposerPicture which in some cases leaves artifacts under windows. The Pixmap will have a transform applied to it so that it is rendered with the same resolution as the composition. Args: * theImage: QImage that will be rendered to the layout. * theWidthMM: int - desired width in mm of output on page. * theLeftOffset: int - offset from left of page. * theTopOffset: int - offset from top of page. Returns: QGraphicsSceneItem is returned Raises: None """ LOGGER.debug('InaSAFE Map drawImage called') myDesiredWidthMM = theWidthMM # mm myDesiredWidthPX = mmToPoints(myDesiredWidthMM, self.pageDpi) myActualWidthPX = theImage.width() myScaleFactor = myDesiredWidthPX / myActualWidthPX LOGGER.debug('%s %s %s' % (myScaleFactor, myActualWidthPX, myDesiredWidthPX)) myTransform = QtGui.QTransform() myTransform.scale(myScaleFactor, myScaleFactor) myTransform.rotate(0.5) myItem = self.composition.addPixmap(QtGui.QPixmap.fromImage(theImage)) myItem.setTransform(myTransform) myItem.setOffset(theLeftOffset / myScaleFactor, theTopOffset / myScaleFactor) return myItem def drawHostAndTime(self, theTopOffset): """Add a disclaimer to the composition. Args: theTopOffset - vertical offset at which to begin drawing Returns: None Raises: None """ LOGGER.debug('InaSAFE Map drawDisclaimer called') #elapsed_time: 11.612545 #user: timlinux #host_name: ultrabook #time_stamp: 2012-10-13_23:10:31 #myUser = self.keywordIO.readKeywords(self.layer, 'user') #myHost = self.keywordIO.readKeywords(self.layer, 'host_name') myDateTime = self.keywordIO.readKeywords(self.layer, 'time_stamp') myTokens = myDateTime.split('_') myDate = myTokens[0] myTime = myTokens[1] #myElapsedTime = self.keywordIO.readKeywords(self.layer, # 'elapsed_time') #myElapsedTime = humaniseSeconds(myElapsedTime) myLongVersion = get_version() myTokens = myLongVersion.split('.') myVersion = '%s.%s.%s' % (myTokens[0], myTokens[1], myTokens[2]) myLabelText = self.tr( 'Date and time of assessment: %1 %2\n' 'Special note: This assessment is a guide - we strongly recommend ' 'that you ground truth the results shown here before deploying ' 'resources and / or personnel.\n' 'Assessment carried out using InaSAFE release %3 (QGIS ' 'plugin version).').arg(myDate).arg(myTime).arg(myVersion) myFontSize = 6 myFontWeight = QtGui.QFont.Normal myItalicsFlag = True myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myLabelText) myLabel.adjustSizeToText() myLabelHeight = 50.0 # mm determined using qgis map composer myLabelWidth = (self.pageWidth / 2) - self.pageMargin myLeftOffset = self.pageWidth / 2 # put in right half of page myLabel.setItemPosition( myLeftOffset, theTopOffset, myLabelWidth, myLabelHeight, ) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel) def drawDisclaimer(self): """Add a disclaimer to the composition. Args: None Returns: None Raises: None """ LOGGER.debug('InaSAFE Map drawDisclaimer called') myFontSize = 10 myFontWeight = QtGui.QFont.Normal myItalicsFlag = True myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(self.disclaimer) myLabel.adjustSizeToText() myLabelHeight = 7.0 # mm determined using qgis map composer myLabelWidth = self.pageWidth # item - position and size...option myLeftOffset = self.pageMargin myTopOffset = self.pageHeight - self.pageMargin myLabel.setItemPosition( myLeftOffset, myTopOffset, myLabelWidth, myLabelHeight, ) myLabel.setFrame(self.showFramesFlag) self.composition.addItem(myLabel) def getMapTitle(self): """Get the map title from the layer keywords if possible. Args: None Returns: None on error, otherwise the title Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map getMapTitle called') try: myTitle = self.keywordIO.readKeywords(self.layer, 'map_title') return myTitle except KeywordNotFoundError: return None except Exception: return None def getMapLegendAtributes(self): """Get the map legend attribute from the layer keywords if possible. Args: None Returns: None on error, otherwise the attributes (notes and units) Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map getMapLegendAtributes called') legendAttributes = ['legend_notes', 'legend_units', 'legend_title'] dictLegendAttributes = {} for myLegendAttribute in legendAttributes: try: dictLegendAttributes[myLegendAttribute] = \ self.keywordIO.readKeywords(self.layer, myLegendAttribute) except KeywordNotFoundError: pass except Exception: pass return dictLegendAttributes def showComposer(self): """Show the composition in a composer view so the user can tweak it if they want to. Args: None Returns: None Raises: None """ myView = QgsComposerView(self.iface.mainWindow()) myView.show() def writeTemplate(self, theTemplateFilePath): """Write the current composition as a template that can be re-used in QGIS.""" myDocument = QtXml.QDomDocument() myElement = myDocument.createElement('Composer') myDocument.appendChild(myElement) self.composition.writeXML(myElement, myDocument) myXml = myDocument.toByteArray() myFile = file(theTemplateFilePath, 'wb') myFile.write(myXml) myFile.close() def renderTemplate(self, theTemplateFilePath, theOutputFilePath): """Load a QgsComposer map from a template and render it .. note:: THIS METHOD IS EXPERIMENTAL AND CURRENTLY NON FUNCTIONAL Args: theTemplateFilePath - path to the template that should be loaded. theOutputFilePath - path for the output pdf Returns: None Raises: None """ self.setupComposition() myResolution = self.composition.printResolution() self.printer = setupPrinter(theOutputFilePath, theResolution=myResolution) if self.composition: myFile = QtCore.QFile(theTemplateFilePath) myDocument = QtXml.QDomDocument() myDocument.setContent(myFile, False) # .. todo:: fix magic param myNodeList = myDocument.elementsByTagName('Composer') if myNodeList.size() > 0: myElement = myNodeList.at(0).toElement() self.composition.readXML(myElement, myDocument) self.printToPdf(theOutputFilePath)
class TestComposerBase(TestQgsPalLabeling): layer = None """:type: QgsVectorLayer""" @classmethod def setUpClass(cls): if not cls._BaseSetup: TestQgsPalLabeling.setUpClass() # the blue background (set via layer style) to match renderchecker's TestQgsPalLabeling.loadFeatureLayer('background', True) cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) @classmethod def tearDownClass(cls): """Run after all tests""" TestQgsPalLabeling.tearDownClass() cls.removeMapLayer(cls.layer) cls.layer = None def setUp(self): """Run before each test.""" super(TestComposerBase, self).setUp() self._TestImage = '' # ensure per test map settings stay encapsulated self._TestMapSettings = self.cloneMapSettings(self._MapSettings) self._Mismatch = 0 self._ColorTol = 0 self._Mismatches.clear() self._ColorTols.clear() def _set_up_composition(self, width, height, dpi): # set up composition and add map self._c = QgsComposition(self._TestMapSettings, QgsProject.instance()) """:type: QgsComposition""" # self._c.setUseAdvancedEffects(False) self._c.setPrintResolution(dpi) # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi paperw = width * 25.4 / dpi paperh = height * 25.4 / dpi self._c.setPaperSize(paperw, paperh) # NOTE: do not use QgsComposerMap(self._c, 0, 0, paperw, paperh) since # it only takes integers as parameters and the composition will grow # larger based upon union of item scene rectangles and a slight buffer # see end of QgsComposition::compositionBounds() # add map as small graphics item first, then set its scene QRectF later self._cmap = QgsComposerMap(self._c, 10, 10, 10, 10) """:type: QgsComposerMap""" self._cmap.setPreviewMode(QgsComposerMap.Render) self._cmap.setFrameEnabled(False) self._c.addComposerMap(self._cmap) # now expand map to fill page and set its extent self._cmap.setSceneRect(QRectF(0, 0, paperw, paperw)) self._cmap.setNewExtent(self.aoiExtent()) # self._cmap.updateCachedImage() self._c.setPlotStyle(QgsComposition.Print) # noinspection PyUnusedLocal def _get_composer_image(self, width, height, dpi): image = QImage(QSize(width, height), self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) self._c.renderPage(p, 0) p.end() # image = self._c.printPageAsRaster(0) # """:type: QImage""" if image.isNull(): return False, '' filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' return res, filepath def _get_composer_svg_image(self, width, height, dpi): # from qgscomposer.cpp, QgsComposer::on_mActionExportAsSVG_triggered, # near end of function svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) self._c.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing) ) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath def _get_composer_pdf_image(self, width, height, dpi): pdfpath = getTempfilePath('pdf') temp_size = os.path.getsize(pdfpath) p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(pdfpath) p.setPaperSize(QSizeF(self._c.paperWidth(), self._c.paperHeight()), QPrinter.Millimeter) p.setFullPage(True) p.setColorMode(QPrinter.Color) p.setResolution(self._c.printResolution()) pdf_p = QPainter(p) # page_mm = p.pageRect(QPrinter.Millimeter) # page_px = p.pageRect(QPrinter.DevicePixel) # self._c.render(pdf_p, page_px, page_mm) self._c.renderPage(pdf_p, 0) pdf_p.end() if temp_size == os.path.getsize(pdfpath): return False, '' filepath = getTempfilePath('png') # Poppler (pdftocairo or pdftoppm): # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase # muPDF (mudraw): # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf if PDFUTIL.strip().endswith('pdftocairo'): filebase = os.path.join( os.path.dirname(filepath), os.path.splitext(os.path.basename(filepath))[0] ) call = [ PDFUTIL, '-png', '-singlefile', '-r', str(dpi), '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), pdfpath, filebase ] elif PDFUTIL.strip().endswith('mudraw'): call = [ PDFUTIL, '-c', 'rgba', '-r', str(dpi), '-w', str(width), '-h', str(height), # '-b', '8', '-o', filepath, pdfpath ] else: return False, '' qDebug("_get_composer_pdf_image call: {0}".format(' '.join(call))) res = False try: subprocess.check_call(call) res = True except subprocess.CalledProcessError as e: qDebug("_get_composer_pdf_image failed!\n" "cmd: {0}\n" "returncode: {1}\n" "message: {2}".format(e.cmd, e.returncode, e.message)) if not res: os.unlink(filepath) filepath = '' return res, filepath def get_composer_output(self, kind): ms = self._TestMapSettings osize = ms.outputSize() width, height, dpi = osize.width(), osize.height(), ms.outputDpi() self._set_up_composition(width, height, dpi) if kind == OutputKind.Svg: return self._get_composer_svg_image(width, height, dpi) elif kind == OutputKind.Pdf: return self._get_composer_pdf_image(width, height, dpi) else: # OutputKind.Img return self._get_composer_image(width, height, dpi) # noinspection PyUnusedLocal def checkTest(self, **kwargs): self.lyr.writeToLayer(self.layer) ms = self._MapSettings # class settings settings_type = 'Class' if self._TestMapSettings is not None: ms = self._TestMapSettings # per test settings settings_type = 'Test' if 'PAL_VERBOSE' in os.environ: qDebug('MapSettings type: {0}'.format(settings_type)) qDebug(mapSettingsString(ms)) res_m, self._TestImage = self.get_composer_output(self._TestKind) self.assertTrue(res_m, 'Failed to retrieve/save output from composer') self.saveControlImage(self._TestImage) mismatch = 0 if 'PAL_NO_MISMATCH' not in os.environ: # some mismatch expected mismatch = self._Mismatch if self._Mismatch else 20 if self._TestGroup in self._Mismatches: mismatch = self._Mismatches[self._TestGroup] colortol = 0 if 'PAL_NO_COLORTOL' not in os.environ: colortol = self._ColorTol if self._ColorTol else 0 if self._TestGroup in self._ColorTols: colortol = self._ColorTols[self._TestGroup] self.assertTrue(*self.renderCheck(mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))
class Map(): """A class for creating a map.""" def __init__(self, iface): """Constructor for the Map class. :param iface: Reference to the QGIS iface object. :type iface: QgsAppInterface """ LOGGER.debug('InaSAFE Map class initialised') self.iface = iface self.layer = iface.activeLayer() self.keywordIO = KeywordIO() self.printer = None self.composition = None self.legend = None self.pageWidth = 210 # width in mm self.pageHeight = 297 # height in mm self.pageDpi = 300.0 self.pageMargin = 10 # margin in mm self.verticalSpacing = 1 # vertical spacing between elements self.showFramesFlag = False # intended for debugging use only # make a square map where width = height = page width self.mapHeight = self.pageWidth - (self.pageMargin * 2) self.mapWidth = self.mapHeight self.disclaimer = self.tr('InaSAFE has been jointly developed by' ' BNPB, AusAid & the World Bank') def tr(self, string): """We implement this since we do not inherit QObject. :param string: String for translation. :type string: QString, str :returns: Translated version of theString. :rtype: QString """ # noinspection PyCallByClass,PyTypeChecker,PyArgumentList return QtCore.QCoreApplication.translate('Map', string) def set_impact_layer(self, layer): """Set the layer that will be used for stats, legend and reporting. :param layer: Layer that will be used for stats, legend and reporting. :type layer: QgsMapLayer, QgsRasterLayer, QgsVectorLayer """ self.layer = layer def setup_composition(self): """Set up the composition ready for drawing elements onto it.""" LOGGER.debug('InaSAFE Map setupComposition called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() self.composition = QgsComposition(myRenderer) self.composition.setPlotStyle(QgsComposition.Print) # or preview self.composition.setPaperSize(self.pageWidth, self.pageHeight) self.composition.setPrintResolution(self.pageDpi) self.composition.setPrintAsRaster(True) def compose_map(self): """Place all elements on the map ready for printing.""" self.setup_composition() # Keep track of our vertical positioning as we work our way down # the page placing elements on it. myTopOffset = self.pageMargin self.draw_logo(myTopOffset) myLabelHeight = self.draw_title(myTopOffset) # Update the map offset for the next row of content myTopOffset += myLabelHeight + self.verticalSpacing myComposerMap = self.draw_map(myTopOffset) self.draw_scalebar(myComposerMap, myTopOffset) # Update the top offset for the next horizontal row of items myTopOffset += self.mapHeight + self.verticalSpacing - 1 myImpactTitleHeight = self.draw_impact_title(myTopOffset) # Update the top offset for the next horizontal row of items if myImpactTitleHeight: myTopOffset += myImpactTitleHeight + self.verticalSpacing + 2 self.draw_legend(myTopOffset) self.draw_host_and_time(myTopOffset) self.draw_disclaimer() def render(self): """Render the map composition to an image and save that to disk. :returns: A three-tuple of: * str: myImagePath - absolute path to png of rendered map * QImage: myImage - in memory copy of rendered map * QRectF: myTargetArea - dimensions of rendered map :rtype: tuple """ LOGGER.debug('InaSAFE Map renderComposition called') # NOTE: we ignore self.composition.printAsRaster() and always rasterise myWidth = int(self.pageDpi * self.pageWidth / 25.4) myHeight = int(self.pageDpi * self.pageHeight / 25.4) myImage = QtGui.QImage(QtCore.QSize(myWidth, myHeight), QtGui.QImage.Format_ARGB32) myImage.setDotsPerMeterX(dpi_to_meters(self.pageDpi)) myImage.setDotsPerMeterY(dpi_to_meters(self.pageDpi)) # Only works in Qt4.8 #myImage.fill(QtGui.qRgb(255, 255, 255)) # Works in older Qt4 versions myImage.fill(55 + 255 * 256 + 255 * 256 * 256) myImagePainter = QtGui.QPainter(myImage) mySourceArea = QtCore.QRectF(0, 0, self.pageWidth, self.pageHeight) myTargetArea = QtCore.QRectF(0, 0, myWidth, myHeight) self.composition.render(myImagePainter, myTargetArea, mySourceArea) myImagePainter.end() myImagePath = unique_filename(prefix='mapRender_', suffix='.png', dir=temp_dir()) myImage.save(myImagePath) return myImagePath, myImage, myTargetArea def make_pdf(self, filename): """Generate the printout for our final map. :param filename: Path on the file system to which the pdf should be saved. If None, a generated file name will be used. :type filename: str :returns: File name of the output file (equivalent to filename if provided). :rtype: str """ LOGGER.debug('InaSAFE Map printToPdf called') if filename is None: myMapPdfPath = unique_filename( prefix='report', suffix='.pdf', dir=temp_dir('work')) else: # We need to cast to python string in case we receive a QString myMapPdfPath = str(filename) self.compose_map() self.printer = setup_printer(myMapPdfPath) _, myImage, myRectangle = self.render() myPainter = QtGui.QPainter(self.printer) myPainter.drawImage(myRectangle, myImage, myRectangle) myPainter.end() return myMapPdfPath def draw_logo(self, top_offset): """Add a picture containing the logo to the map top left corner :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ myLogo = QgsComposerPicture(self.composition) myLogo.setPictureFile(':/plugins/inasafe/bnpb_logo.png') myLogo.setItemPosition(self.pageMargin, top_offset, 10, 10) myLogo.setFrameEnabled(self.showFramesFlag) myLogo.setZValue(1) # To ensure it overlays graticule markers self.composition.addItem(myLogo) def draw_title(self, top_offset): """Add a title to the composition. :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int :returns: The height of the label as rendered. :rtype: float """ LOGGER.debug('InaSAFE Map drawTitle called') myFontSize = 14 myFontWeight = QtGui.QFont.Bold myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myHeading = self.tr('InaSAFE - Indonesia Scenario Assessment' ' for Emergencies') myLabel.setText(myHeading) myLabel.adjustSizeToText() myLabelHeight = 10.0 # determined using qgis map composer myLabelWidth = 170.0 # item - position and size...option myLeftOffset = self.pageWidth - self.pageMargin - myLabelWidth myLabel.setItemPosition(myLeftOffset, top_offset - 2, # -2 to push it up a little myLabelWidth, myLabelHeight, ) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel) return myLabelHeight def draw_map(self, top_offset): """Add a map to the composition and return the composer map instance. :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int :returns: The composer map. :rtype: QgsComposerMap """ LOGGER.debug('InaSAFE Map drawMap called') myMapWidth = self.mapWidth myComposerMap = QgsComposerMap( self.composition, self.pageMargin, top_offset, myMapWidth, self.mapHeight) #myExtent = self.iface.mapCanvas().extent() # The dimensions of the map canvas and the print composer map may # differ. So we set the map composer extent using the canvas and # then defer to the map canvas's map extents thereafter # Update: disabled as it results in a rectangular rather than # square map #myComposerMap.setNewExtent(myExtent) myComposerExtent = myComposerMap.extent() # Recenter the composer map on the center of the canvas # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge myCanvasExtent = self.iface.mapCanvas().extent() myWidth = myCanvasExtent.width() myHeight = myCanvasExtent.height() myLongestLength = myWidth if myWidth < myHeight: myLongestLength = myHeight myHalfLength = myLongestLength / 2 myCenter = myCanvasExtent.center() myMinX = myCenter.x() - myHalfLength myMaxX = myCenter.x() + myHalfLength myMinY = myCenter.y() - myHalfLength myMaxY = myCenter.y() + myHalfLength mySquareExtent = QgsRectangle(myMinX, myMinY, myMaxX, myMaxY) myComposerMap.setNewExtent(mySquareExtent) myComposerMap.setGridEnabled(True) myNumberOfSplits = 5 # .. todo:: Write logic to adjust precision so that adjacent tick marks # always have different displayed values myPrecision = 2 myXInterval = myComposerExtent.width() / myNumberOfSplits myComposerMap.setGridIntervalX(myXInterval) myYInterval = myComposerExtent.height() / myNumberOfSplits myComposerMap.setGridIntervalY(myYInterval) myComposerMap.setGridStyle(QgsComposerMap.Cross) myCrossLengthMM = 1 myComposerMap.setCrossLength(myCrossLengthMM) myComposerMap.setZValue(0) # To ensure it does not overlay logo myFontSize = 6 myFontWeight = QtGui.QFont.Normal myItalicsFlag = False myFont = QtGui.QFont( 'verdana', myFontSize, myFontWeight, myItalicsFlag) myComposerMap.setGridAnnotationFont(myFont) myComposerMap.setGridAnnotationPrecision(myPrecision) myComposerMap.setShowGridAnnotation(True) myComposerMap.setGridAnnotationDirection( QgsComposerMap.BoundaryDirection, QgsComposerMap.Top) self.composition.addItem(myComposerMap) self.draw_graticule_mask(top_offset) return myComposerMap def draw_graticule_mask(self, top_offset): """A helper function to mask out graticule labels. It will hide labels on the right side by over painting a white rectangle with white border on them. **kludge** :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawGraticuleMask called') myLeftOffset = self.pageMargin + self.mapWidth myRect = QgsComposerShape(myLeftOffset + 0.5, top_offset, self.pageWidth - myLeftOffset, self.mapHeight + 1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setColor(QtGui.QColor(0, 0, 0)) myPen.setWidthF(0.1) myRect.setPen(myPen) myRect.setBackgroundColor(QtGui.QColor(255, 255, 255)) myRect.setTransparency(100) #myRect.setLineWidth(0.1) #myRect.setFrameEnabled(False) #myRect.setOutlineColor(QtGui.QColor(255, 255, 255)) #myRect.setFillColor(QtGui.QColor(255, 255, 255)) #myRect.setOpacity(100) # These two lines seem superfluous but are needed myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) myRect.setBrush(myBrush) self.composition.addItem(myRect) def draw_native_scalebar(self, composer_map, top_offset): """Draw a scale bar using QGIS' native drawing. In the case of geographic maps, scale will be in degrees, not km. :param composer_map: Composer map on which to draw the scalebar. :type composer_map: QgsComposerMap :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawNativeScaleBar called') myScaleBar = QgsComposerScaleBar(self.composition) myScaleBar.setStyle('Numeric') # optionally modify the style myScaleBar.setComposerMap(composer_map) myScaleBar.applyDefaultSize() myScaleBarHeight = myScaleBar.boundingRect().height() myScaleBarWidth = myScaleBar.boundingRect().width() # -1 to avoid overlapping the map border myScaleBar.setItemPosition( self.pageMargin + 1, top_offset + self.mapHeight - (myScaleBarHeight * 2), myScaleBarWidth, myScaleBarHeight) myScaleBar.setFrameEnabled(self.showFramesFlag) # Disabled for now #self.composition.addItem(myScaleBar) def draw_scalebar(self, composer_map, top_offset): """Add a numeric scale to the bottom left of the map. We draw the scale bar manually because QGIS does not yet support rendering a scale bar for a geographic map in km. .. seealso:: :meth:`drawNativeScaleBar` :param composer_map: Composer map on which to draw the scalebar. :type composer_map: QgsComposerMap :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawScaleBar called') myCanvas = self.iface.mapCanvas() myRenderer = myCanvas.mapRenderer() # # Add a linear map scale # myDistanceArea = QgsDistanceArea() myDistanceArea.setSourceCrs(myRenderer.destinationCrs().srsid()) myDistanceArea.setEllipsoidalMode(True) # Determine how wide our map is in km/m # Starting point at BL corner myComposerExtent = composer_map.extent() myStartPoint = QgsPoint(myComposerExtent.xMinimum(), myComposerExtent.yMinimum()) # Ending point at BR corner myEndPoint = QgsPoint(myComposerExtent.xMaximum(), myComposerExtent.yMinimum()) myGroundDistance = myDistanceArea.measureLine(myStartPoint, myEndPoint) # Get the equivalent map distance per page mm myMapWidth = self.mapWidth # How far is 1mm on map on the ground in meters? myMMToGroundDistance = myGroundDistance / myMapWidth #print 'MM:', myMMDistance # How long we want the scale bar to be in relation to the map myScaleBarToMapRatio = 0.5 # How many divisions the scale bar should have myTickCount = 5 myScaleBarWidthMM = myMapWidth * myScaleBarToMapRatio myPrintSegmentWidthMM = myScaleBarWidthMM / myTickCount # Segment width in real world (m) # We apply some logic here so that segments are displayed in meters # if each segment is less that 1000m otherwise km. Also the segment # lengths are rounded down to human looking numbers e.g. 1km not 1.1km myUnits = '' myGroundSegmentWidth = myPrintSegmentWidthMM * myMMToGroundDistance if myGroundSegmentWidth < 1000: myUnits = 'm' myGroundSegmentWidth = round(myGroundSegmentWidth) # adjust the segment width now to account for rounding myPrintSegmentWidthMM = myGroundSegmentWidth / myMMToGroundDistance else: myUnits = 'km' # Segment with in real world (km) myGroundSegmentWidth = round(myGroundSegmentWidth / 1000) myPrintSegmentWidthMM = ((myGroundSegmentWidth * 1000) / myMMToGroundDistance) # Now adjust the scalebar width to account for rounding myScaleBarWidthMM = myTickCount * myPrintSegmentWidthMM #print "SBWMM:", myScaleBarWidthMM #print "SWMM:", myPrintSegmentWidthMM #print "SWM:", myGroundSegmentWidthM #print "SWKM:", myGroundSegmentWidthKM # start drawing in line segments myScaleBarHeight = 5 # mm myLineWidth = 0.3 # mm myInsetDistance = 7 # how much to inset the scalebar into the map by myScaleBarX = self.pageMargin + myInsetDistance myScaleBarY = ( top_offset + self.mapHeight - myInsetDistance - myScaleBarHeight) # mm # Draw an outer background box - shamelessly hardcoded buffer myRect = QgsComposerShape(myScaleBarX - 4, # left edge myScaleBarY - 3, # top edge myScaleBarWidthMM + 13, # right edge myScaleBarHeight + 6, # bottom edge self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setColor(QtGui.QColor(255, 255, 255)) myPen.setWidthF(myLineWidth) myRect.setPen(myPen) #myRect.setLineWidth(myLineWidth) myRect.setFrameEnabled(False) myBrush = QtGui.QBrush(QtGui.QColor(255, 255, 255)) # workaround for missing setTransparentFill missing from python api myRect.setBrush(myBrush) self.composition.addItem(myRect) # Set up the tick label font myFontWeight = QtGui.QFont.Normal myFontSize = 6 myItalicsFlag = False myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) # Draw the bottom line myUpshift = 0.3 # shift the bottom line up for better rendering myRect = QgsComposerShape(myScaleBarX, myScaleBarY + myScaleBarHeight - myUpshift, myScaleBarWidthMM, 0.1, self.composition) myRect.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setColor(QtGui.QColor(255, 255, 255)) myPen.setWidthF(myLineWidth) myRect.setPen(myPen) #myRect.setLineWidth(myLineWidth) myRect.setFrameEnabled(False) self.composition.addItem(myRect) # Now draw the scalebar ticks for myTickCountIterator in range(0, myTickCount + 1): myDistanceSuffix = '' if myTickCountIterator == myTickCount: myDistanceSuffix = ' ' + myUnits myRealWorldDistance = ('%.0f%s' % (myTickCountIterator * myGroundSegmentWidth, myDistanceSuffix)) #print 'RW:', myRealWorldDistance myMMOffset = myScaleBarX + (myTickCountIterator * myPrintSegmentWidthMM) #print 'MM:', myMMOffset myTickHeight = myScaleBarHeight / 2 # Lines are not exposed by the api yet so we # bodge drawing lines using rectangles with 1px height or width myTickWidth = 0.1 # width or rectangle to be drawn myUpTickLine = QgsComposerShape( myMMOffset, myScaleBarY + myScaleBarHeight - myTickHeight, myTickWidth, myTickHeight, self.composition) myUpTickLine.setShapeType(QgsComposerShape.Rectangle) myPen = QtGui.QPen() myPen.setWidthF(myLineWidth) myUpTickLine.setPen(myPen) #myUpTickLine.setLineWidth(myLineWidth) myUpTickLine.setFrameEnabled(False) self.composition.addItem(myUpTickLine) # # Add a tick label # myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myRealWorldDistance) myLabel.adjustSizeToText() myLabel.setItemPosition( myMMOffset - 3, myScaleBarY - myTickHeight) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel) def draw_impact_title(self, top_offset): """Draw the map subtitle - obtained from the impact layer keywords. :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int :returns: The height of the label as rendered. :rtype: float """ LOGGER.debug('InaSAFE Map drawImpactTitle called') myTitle = self.map_title() if myTitle is None: myTitle = '' myFontSize = 20 myFontWeight = QtGui.QFont.Bold myItalicsFlag = False myFont = QtGui.QFont( 'verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myHeading = myTitle myLabel.setText(myHeading) myLabelWidth = self.pageWidth - (self.pageMargin * 2) myLabelHeight = 12 myLabel.setItemPosition( self.pageMargin, top_offset, myLabelWidth, myLabelHeight) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel) return myLabelHeight def draw_legend(self, top_offset): """Add a legend to the map using our custom legend renderer. .. note:: getLegend generates a pixmap in 150dpi so if you set the map to a higher dpi it will appear undersized. :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawLegend called') mapLegendAttributes = self.map_legend_attributes() legendNotes = mapLegendAttributes.get('legend_notes', None) legendUnits = mapLegendAttributes.get('legend_units', None) legendTitle = mapLegendAttributes.get('legend_title', None) LOGGER.debug(mapLegendAttributes) myLegend = MapLegend(self.layer, self.pageDpi, legendTitle, legendNotes, legendUnits) self.legend = myLegend.get_legend() myPicture1 = QgsComposerPicture(self.composition) myLegendFilePath = unique_filename( prefix='legend', suffix='.png', dir='work') self.legend.save(myLegendFilePath, 'PNG') myPicture1.setPictureFile(myLegendFilePath) myLegendHeight = points_to_mm(self.legend.height(), self.pageDpi) myLegendWidth = points_to_mm(self.legend.width(), self.pageDpi) myPicture1.setItemPosition(self.pageMargin, top_offset, myLegendWidth, myLegendHeight) myPicture1.setFrameEnabled(False) self.composition.addItem(myPicture1) os.remove(myLegendFilePath) def draw_image(self, theImage, theWidthMM, theLeftOffset, theTopOffset): """Helper to draw an image directly onto the QGraphicsScene. This is an alternative to using QgsComposerPicture which in some cases leaves artifacts under windows. The Pixmap will have a transform applied to it so that it is rendered with the same resolution as the composition. :param theImage: Image that will be rendered to the layout. :type theImage: QImage :param theWidthMM: Desired width in mm of output on page. :type theWidthMM: int :param theLeftOffset: Offset from left of page. :type theLeftOffset: int :param theTopOffset: Offset from top of page. :type theTopOffset: int :returns: Graphics scene item. :rtype: QGraphicsSceneItem """ LOGGER.debug('InaSAFE Map drawImage called') myDesiredWidthMM = theWidthMM # mm myDesiredWidthPX = mm_to_points(myDesiredWidthMM, self.pageDpi) myActualWidthPX = theImage.width() myScaleFactor = myDesiredWidthPX / myActualWidthPX LOGGER.debug('%s %s %s' % ( myScaleFactor, myActualWidthPX, myDesiredWidthPX)) myTransform = QtGui.QTransform() myTransform.scale(myScaleFactor, myScaleFactor) myTransform.rotate(0.5) # noinspection PyArgumentList myItem = self.composition.addPixmap(QtGui.QPixmap.fromImage(theImage)) myItem.setTransform(myTransform) myItem.setOffset(theLeftOffset / myScaleFactor, theTopOffset / myScaleFactor) return myItem def draw_host_and_time(self, top_offset): """Add a note with hostname and time to the composition. :param top_offset: Vertical offset at which the logo should be drawn. :type top_offset: int """ LOGGER.debug('InaSAFE Map drawDisclaimer called') #elapsed_time: 11.612545 #user: timlinux #host_name: ultrabook #time_stamp: 2012-10-13_23:10:31 #myUser = self.keywordIO.readKeywords(self.layer, 'user') #myHost = self.keywordIO.readKeywords(self.layer, 'host_name') myDateTime = self.keywordIO.read_keywords(self.layer, 'time_stamp') myTokens = myDateTime.split('_') myDate = myTokens[0] myTime = myTokens[1] #myElapsedTime = self.keywordIO.readKeywords(self.layer, # 'elapsed_time') #myElapsedTime = humaniseSeconds(myElapsedTime) myLongVersion = get_version() myTokens = myLongVersion.split('.') myVersion = '%s.%s.%s' % (myTokens[0], myTokens[1], myTokens[2]) myLabelText = self.tr( 'Date and time of assessment: %s %s\n' 'Special note: This assessment is a guide - we strongly recommend ' 'that you ground truth the results shown here before deploying ' 'resources and / or personnel.\n' 'Assessment carried out using InaSAFE release %s (QGIS ' 'plugin version).') % (myDate, myTime, myVersion) myFontSize = 6 myFontWeight = QtGui.QFont.Normal myItalicsFlag = True myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(myLabelText) myLabel.adjustSizeToText() myLabelHeight = 50.0 # mm determined using qgis map composer myLabelWidth = (self.pageWidth / 2) - self.pageMargin myLeftOffset = self.pageWidth / 2 # put in right half of page myLabel.setItemPosition(myLeftOffset, top_offset, myLabelWidth, myLabelHeight, ) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel) def draw_disclaimer(self): """Add a disclaimer to the composition.""" LOGGER.debug('InaSAFE Map drawDisclaimer called') myFontSize = 10 myFontWeight = QtGui.QFont.Normal myItalicsFlag = True myFont = QtGui.QFont('verdana', myFontSize, myFontWeight, myItalicsFlag) myLabel = QgsComposerLabel(self.composition) myLabel.setFont(myFont) myLabel.setText(self.disclaimer) myLabel.adjustSizeToText() myLabelHeight = 7.0 # mm determined using qgis map composer myLabelWidth = self.pageWidth # item - position and size...option myLeftOffset = self.pageMargin myTopOffset = self.pageHeight - self.pageMargin myLabel.setItemPosition(myLeftOffset, myTopOffset, myLabelWidth, myLabelHeight, ) myLabel.setFrameEnabled(self.showFramesFlag) self.composition.addItem(myLabel) def map_title(self): """Get the map title from the layer keywords if possible. :returns: None on error, otherwise the title. :rtype: None, str """ LOGGER.debug('InaSAFE Map getMapTitle called') try: myTitle = self.keywordIO.read_keywords(self.layer, 'map_title') return myTitle except KeywordNotFoundError: return None except Exception: return None def map_legend_attributes(self): """Get the map legend attribute from the layer keywords if possible. :returns: None on error, otherwise the attributes (notes and units). :rtype: None, str """ LOGGER.debug('InaSAFE Map getMapLegendAtributes called') legendAttributes = ['legend_notes', 'legend_units', 'legend_title'] dictLegendAttributes = {} for myLegendAttribute in legendAttributes: try: dictLegendAttributes[myLegendAttribute] = \ self.keywordIO.read_keywords(self.layer, myLegendAttribute) except KeywordNotFoundError: pass except Exception: pass return dictLegendAttributes def showComposer(self): """Show the composition in a composer view so the user can tweak it. """ myView = QgsComposerView(self.iface.mainWindow()) myView.show() def write_template(self, template_path): """Write current composition as a template that can be re-used in QGIS. :param template_path: Path to which template should be written. :type template_path: str """ myDocument = QtXml.QDomDocument() myElement = myDocument.createElement('Composer') myDocument.appendChild(myElement) self.composition.writeXML(myElement, myDocument) myXml = myDocument.toByteArray() myFile = file(template_path, 'wb') myFile.write(myXml) myFile.close() def render_template(self, template_path, output_path): """Load a QgsComposer map from a template and render it. .. note:: THIS METHOD IS EXPERIMENTAL AND CURRENTLY NON FUNCTIONAL :param template_path: Path to the template that should be loaded. :type template_path: str :param output_path: Path for the output pdf. :type output_path: str """ self.setup_composition() myResolution = self.composition.printResolution() self.printer = setup_printer( output_path, resolution=myResolution) if self.composition: myFile = QtCore.QFile(template_path) myDocument = QtXml.QDomDocument() myDocument.setContent(myFile, False) # .. todo:: fix magic param myNodeList = myDocument.elementsByTagName('Composer') if myNodeList.size() > 0: myElement = myNodeList.at(0).toElement() self.composition.readXML(myElement, myDocument) self.make_pdf(output_path)
class TestQgsComposerMap(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition with composer map self.mMapSettings = QgsMapSettings() crs = QgsCoordinateReferenceSystem(32633) self.mMapSettings.setDestinationCrs(crs) self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposerMap.setBackgroundColor(QColor(150, 100, 100)) self.mComposition.addComposerMap(self.mComposerMap) def testGrid(self): """Test that we can create a grid for a map.""" myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(True) self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setAnnotationFont(QgsFontUtils.getStandardTestFont()) self.mComposerMap.grid().setAnnotationPrecision(0) self.mComposerMap.grid().setAnnotationDisplay(QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Left) self.mComposerMap.grid().setAnnotationPosition(QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Right) self.mComposerMap.grid().setAnnotationDisplay(QgsComposerMapGrid.HideAll, QgsComposerMapGrid.Top) self.mComposerMap.grid().setAnnotationPosition(QgsComposerMapGrid.OutsideMapFrame, QgsComposerMapGrid.Bottom) self.mComposerMap.grid().setAnnotationDirection(QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Right) self.mComposerMap.grid().setAnnotationDirection(QgsComposerMapGrid.Horizontal, QgsComposerMapGrid.Bottom) self.mComposerMap.grid().setAnnotationFontColor(QColor(255, 0, 0, 150)) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_Overlay) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_grid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testCrossGrid(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Cross) self.mComposerMap.grid().setCrossLength(2.0) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setGridLineColor(QColor(0, 255, 0)) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_crossgrid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testMarkerGrid(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Markers) self.mComposerMap.grid().setCrossLength(2.0) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_markergrid', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) assert myTestResult, myMessage def testFrameOnly(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.FrameAnnotationsOnly) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFramePenSize(0.5) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_gridframeonly', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition() self.mComposerMap.grid().setStyle(QgsComposerMapGrid.Solid) self.mComposerMap.grid().setEnabled(False) self.mComposerMap.grid().setAnnotationEnabled(False) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) assert myTestResult, myMessage def testZebraStyle(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setFramePenColor(QColor(255, 100, 0, 200)) self.mComposerMap.grid().setFrameFillColor1(QColor(50, 90, 50, 100)) self.mComposerMap.grid().setFrameFillColor2(QColor(200, 220, 100, 60)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage def testZebraStyleSides(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setGridLineColor(QColor(0, 0, 0)) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setGridLineWidth(0.5) self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) self.mComposerMap.grid().setFrameFillColor1(QColor(0, 0, 0)) self.mComposerMap.grid().setFrameFillColor2(QColor(255, 255, 255)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameLeft, True) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameRight, False) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, False) self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameBottom, False) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_left', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameTop, True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_lefttop', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameRight, True) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_zebrastyle_lefttopright', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage self.mComposerMap.grid().setFrameSideFlag(QgsComposerMapGrid.FrameBottom, True) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.NoFrame) def testInteriorTicks(self): self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.Zebra) myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.grid().setIntervalX(2000) self.mComposerMap.grid().setIntervalY(2000) self.mComposerMap.grid().setAnnotationFontColor(QColor(0, 0, 0)) self.mComposerMap.grid().setBlendMode(QPainter.CompositionMode_SourceOver) self.mComposerMap.grid().setFrameStyle(QgsComposerMapGrid.InteriorTicks) self.mComposerMap.grid().setFrameWidth(10) self.mComposerMap.grid().setFramePenSize(1) self.mComposerMap.grid().setFramePenColor(QColor(0, 0, 0)) self.mComposerMap.grid().setEnabled(True) self.mComposerMap.grid().setStyle(QgsComposerMapGrid.FrameAnnotationsOnly) self.mComposerMap.updateBoundingRect() checker = QgsCompositionChecker('composermap_interiorticks', self.mComposition) checker.setControlPathPrefix("composer_mapgrid") myTestResult, myMessage = checker.testComposition(0, 100) assert myTestResult, myMessage
class TestQgsComposerHtml(TestCase): def setUp(self): """Run before each test.""" self.mapSettings = QgsMapSettings() self.mComposition = QgsComposition(self.mapSettings) self.mComposition.setPaperSize(297, 210) #A4 landscape def tearDown(self): """Run after each test.""" print "Tear down" def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "test_html.html") myUrl = QUrl("file:///" + myPath) return myUrl def testTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker('composerhtml_table', self.mComposition) myTestResult, myMessage = checker.testComposition() qDebug(myMessage) self.mComposition.removeMultiFrame( composerHtml ) composerHtml = None assert myTestResult, myMessage def testTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks( False ) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print "Checking page 1" myPage = 0 checker1 = QgsCompositionChecker('composerhtml_multiframe1', self.mComposition) myTestResult, myMessage = checker1.testComposition( myPage ) assert myTestResult, myMessage print "Checking page 2" myPage = 1 checker2 = QgsCompositionChecker('composerhtml_multiframe2', self.mComposition) myTestResult, myMessage = checker2.testComposition( myPage ) assert myTestResult, myMessage self.mComposition.removeMultiFrame( composerHtml ) composerHtml = None assert myTestResult, myMessage def testHtmlSmartBreaks(self): """Test rendering to multiframes with smart breaks.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 52) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks( True ) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print "Checking page 1" myPage = 0 checker1 = QgsCompositionChecker('composerhtml_smartbreaks1', self.mComposition) myTestResult, myMessage = checker1.testComposition( myPage, 200 ) assert myTestResult, myMessage print "Checking page 2" myPage = 1 checker2 = QgsCompositionChecker('composerhtml_smartbreaks2', self.mComposition) myTestResult, myMessage = checker2.testComposition( myPage, 200 ) assert myTestResult, myMessage self.mComposition.removeMultiFrame( composerHtml ) composerHtml = None assert myTestResult, myMessage def testComposerHtmlAccessor(self): """Test that we can retrieve the ComposerHtml instance given an item. """ myComposition = QgsComposition(CANVAS.mapRenderer()) mySubstitutionMap = {'replace-me': 'Foo bar'} myFile = os.path.join(TEST_DATA_DIR, 'template.qpt') myTemplateFile = file(myFile, 'rt') myTemplateContent = myTemplateFile.read() myTemplateFile.close() myDocument = QDomDocument() myDocument.setContent(myTemplateContent) myComposition.loadFromTemplate(myDocument, mySubstitutionMap) myItem = myComposition.getComposerItemById('html-test') myComposerHtml = myComposition.getComposerHtmlByItem(myItem) myMessage = 'Could not retrieve the composer html given an item' assert myComposerHtml is not None, myMessage
class TestQgsAtlasComposition(unittest.TestCase): def testCase(self): self.TEST_DATA_DIR = unitTestDataPath() vectorFileInfo = QFileInfo( self.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( True ) mMapRenderer.setMapUnits( QGis.Meters ) # select epsg:2154 crs = QgsCoordinateReferenceSystem() crs.createFromSrid( 2154 ) mMapRenderer.setDestinationCrs( crs ) self.mComposition = QgsComposition( mMapRenderer ) 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() def filename_test( self ): self.mAtlas.setFilenamePattern( "'output_' || $feature" ) self.mAtlas.beginRender() for i in range(0, self.mAtlas.numFeatures()): self.mAtlas.prepareForFeature( i ) expected = "output_%d" % (i+1) assert self.mAtlas.currentFilename() == expected self.mAtlas.endRender() def autoscale_render_test( self ): self.mAtlasMap.setAtlasDriven( True ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Auto ) self.mAtlasMap.setAtlasMargin( 0.10 ) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlasMap.setAtlasDriven( False ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Fixed ) self.mAtlasMap.setAtlasMargin( 0 ) def autoscale_render_test_old_api( self ): self.mAtlas.setComposerMap( self.mAtlasMap ) self.mAtlas.setFixedScale( False ) self.mAtlas.setMargin( 0.10 ) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale_old_api%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlas.setFixedScale( True ) self.mAtlas.setMargin( 0 ) self.mAtlas.setComposerMap( None ) self.mAtlasMap.setAtlasDriven( False ) def fixedscale_render_test( self ): self.mAtlasMap.setAtlasDriven( True ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Fixed ) self.mAtlasMap.setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_fixedscale%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def predefinedscales_render_test( self ): self.mAtlasMap.setAtlasDriven( True ) self.mAtlasMap.setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Predefined ) scales = [1800000, 5000000] self.mAtlas.setPredefinedScales( scales ) for i, s in enumerate(self.mAtlas.predefinedScales()): assert s == scales[i] self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_predefinedscales%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def hidden_render_test( self ): self.mAtlasMap.setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Fixed ) self.mAtlas.setHideCoverage( True ) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_hiding%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def sorting_render_test( self ): self.mAtlasMap.setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Fixed ) self.mAtlas.setHideCoverage( False ) self.mAtlas.setSortFeatures( True ) self.mAtlas.setSortKeyAttributeIndex( 4 ) # departement name self.mAtlas.setSortAscending( False ) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_sorting%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def filtering_render_test( self ): self.mAtlasMap.setNewExtent( QgsRectangle( 209838.166, 6528781.020, 610491.166, 6920530.620 ) ) self.mAtlasMap.setAtlasScalingMode( QgsComposerMap.Fixed ) self.mAtlas.setHideCoverage( False ) self.mAtlas.setSortFeatures( False ) self.mAtlas.setFilterFeatures( True ) self.mAtlas.setFeatureFilter( "substr(NAME_1,1,1)='P'" ) # select only 'Pays de la loire' self.mAtlas.beginRender() for i in range(0, 1): self.mAtlas.prepareForFeature( i ) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_filtering%d' % (i + 1), self.mComposition) myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender()
class TestQgsAtlasComposition(unittest.TestCase): 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(QgsUnitTypes.DistanceMeters) # 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 = QgsFillSymbol.createSimple(props) renderer = QgsSingleSymbolRenderer(fillSymbol) mVectorLayer.setRenderer(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.overview().setFrameMap(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 = QgsFillSymbol.createSimple(props2) mOverview.overview().setFrameSymbol(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( "# [%@atlas_featurenumber || ' / ' || @atlas_totalfeatures%]") 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.fixedscale_render_test() self.predefinedscales_render_test() self.hidden_render_test() self.legend_test() shutil.rmtree(tmppath, True) def filename_test(self): self.mAtlas.setFilenamePattern("'output_' || @atlas_featurenumber") self.mAtlas.beginRender() for i in range(0, self.mAtlas.numFeatures()): self.mAtlas.prepareForFeature(i) expected = "output_%d" % (i + 1) assert self.mAtlas.currentFilename() == expected self.mAtlas.endRender() def autoscale_render_test(self): self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) self.mAtlasMap.setAtlasMargin(0.10) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_autoscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlasMap.setAtlasDriven(False) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlasMap.setAtlasMargin(0) def fixedscale_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_fixedscale%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def predefinedscales_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Predefined) scales = [1800000, 5000000] self.mAtlas.setPredefinedScales(scales) for i, s in enumerate(self.mAtlas.predefinedScales()): assert s == scales[i] self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker( 'atlas_predefinedscales%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def hidden_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(True) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_hiding%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() self.mAtlas.setHideCoverage(False) def sorting_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(True) self.mAtlas.setSortKeyAttributeIndex(4) # departement name self.mAtlas.setSortAscending(False) self.mAtlas.beginRender() for i in range(0, 2): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_sorting%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def filtering_render_test(self): self.mAtlasMap.setNewExtent( QgsRectangle(209838.166, 6528781.020, 610491.166, 6920530.620)) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Fixed) self.mAtlas.setHideCoverage(False) self.mAtlas.setSortFeatures(False) self.mAtlas.setFilterFeatures(True) self.mAtlas.setFeatureFilter( "substr(NAME_1,1,1)='P'") # select only 'Pays de la loire' self.mAtlas.beginRender() for i in range(0, 1): self.mAtlas.prepareForFeature(i) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_filtering%d' % (i + 1), self.mComposition) checker.setControlPathPrefix("atlas") myTestResult, myMessage = checker.testComposition(0, 200) assert myTestResult self.mAtlas.endRender() def legend_test(self): self.mAtlasMap.setAtlasDriven(True) self.mAtlasMap.setAtlasScalingMode(QgsComposerMap.Auto) self.mAtlasMap.setAtlasMargin(0.10) # add a point layer ptLayer = QgsVectorLayer( "Point?crs=epsg:4326&field=attr:int(1)&field=label:string(20)", "points", "memory") pr = ptLayer.dataProvider() f1 = QgsFeature(1) f1.initAttributes(2) f1.setAttribute(0, 1) f1.setAttribute(1, "Test label 1") f1.setGeometry(QgsGeometry.fromPoint(QgsPoint(-0.638, 48.954))) f2 = QgsFeature(2) f2.initAttributes(2) f2.setAttribute(0, 2) f2.setAttribute(1, "Test label 2") f2.setGeometry(QgsGeometry.fromPoint(QgsPoint(-1.682, 48.550))) pr.addFeatures([f1, f2]) # categorized symbology r = QgsCategorizedSymbolRenderer("attr", [ QgsRendererCategory( 1, QgsMarkerSymbol.createSimple({"color": "255,0,0"}), "red"), QgsRendererCategory( 2, QgsMarkerSymbol.createSimple({"color": "0,0,255"}), "blue") ]) ptLayer.setRenderer(r) QgsMapLayerRegistry.instance().addMapLayer(ptLayer) # add the point layer to the map settings layers = self.mapSettings.layers() layers = [ptLayer.id()] + layers self.mapSettings.setLayers(layers) # add a legend legend = QgsComposerLegend(self.mComposition) legend.moveBy(200, 100) # sets the legend filter parameter legend.setComposerMap(self.mAtlasMap) legend.setLegendFilterOutAtlas(True) self.mComposition.addComposerLegend(legend) self.mAtlas.beginRender() self.mAtlas.prepareForFeature(0) self.mLabel1.adjustSizeToText() checker = QgsCompositionChecker('atlas_legend', self.mComposition) myTestResult, myMessage = checker.testComposition() assert myTestResult self.mAtlas.endRender() # restore state self.mapSettings.setLayers([layers[1]]) self.mComposition.removeComposerItem(legend) QgsMapLayerRegistry.instance().removeMapLayer(ptLayer.id())
class TestQgsComposerHtml(TestCase): def setUp(self): """Run before each test.""" self.mComposition = QgsComposition(None) self.mComposition.setPaperSize(297, 210) #A4 landscape def tearDown(self): """Run after each test.""" print "Tear down" def controlImagePath(self, theImageName): """Helper to get the path to a control image.""" myPath = os.path.join(TEST_DATA_DIR, "control_images", "expected_composerhtml", theImageName) assert os.path.exists(myPath) return myPath def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "html_table.html") myUrl = QUrl(QString("file:///%1").arg(myPath)) return myUrl @expectedFailure def XtestTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker() myResult, myMessage = checker.testComposition( "Composer html table", self.mComposition, self.controlImagePath("composerhtml_table.png")) qDebug(myMessage) assert myResult, myMessage @expectedFailure def XtestTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode( QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) myPage = 0 checker1 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe1.png") print "Checking page 1" myResult, myMessage = checker1.testComposition("Composer html table", self.mComposition, myControlImage, myPage) assert myResult, myMessage myPage = 1 checker2 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe2.png") print "Checking page 2" myResult, myMessage = checker2.testComposition("Composer html table", self.mComposition, myControlImage, myPage) assert myResult, myMessage myPage = 2 checker3 = QgsCompositionChecker() myControlImage = self.controlImagePath( "composerhtml_table_multiframe3.png") myResult, myMessage = checker3.testComposition("Composer html table", self.mComposition, myControlImage, myPage) print "Checking page 3" assert myResult, myMessage def testComposerHtmlAccessor(self): """Test that we can retrieve the ComposerHtml instance given an item. """ myComposition = QgsComposition(CANVAS.mapRenderer()) mySubstitutionMap = {'replace-me': 'Foo bar'} myFile = os.path.join(TEST_DATA_DIR, 'template.qpt') myTemplateFile = file(myFile, 'rt') myTemplateContent = myTemplateFile.read() myTemplateFile.close() myDocument = QDomDocument() myDocument.setContent(myTemplateContent) myComposition.loadFromTemplate(myDocument, mySubstitutionMap) myItem = myComposition.getComposerItemById('html-test') myComposerHtml = myComposition.getComposerHtmlByItem(myItem) myMessage = 'Could not retrieve the composer html given an item' assert myComposerHtml is not None, myMessage
class TestQgsComposerShapes(TestCase): def __init__(self, methodName): """Run once on class initialisation.""" unittest.TestCase.__init__(self, methodName) # create composition self.mComposition = QgsComposition(None) self.mComposition.setPaperSize(297, 210) self.mComposerShape = QgsComposerShape(20, 20, 150, 100, self.mComposition) self.mComposerShape.setBackgroundColor(QColor.fromRgb(255, 150, 0)) self.mComposition.addComposerShape(self.mComposerShape) def testRectangle(self): """Test rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composershapes', 'composershape_rectangle.png') myTestResult, myMessage = checker.testComposition( 'Composer shapes rectangle', self.mComposition, myPath) assert myTestResult == True, myMessage def testEllipse(self): """Test ellipse composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Ellipse) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composershapes', 'composershape_ellipse.png') myTestResult, myMessage = checker.testComposition( 'Composer shapes ellipse', self.mComposition, myPath) assert myTestResult == True, myMessage def testTriangle(self): """Test triangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Triangle) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composershapes', 'composershape_triangle.png') myTestResult, myMessage = checker.testComposition( 'Composer shapes triangle', self.mComposition, myPath) assert myTestResult == True, myMessage def testRoundedRectangle(self): """Test rounded rectangle composer shape""" self.mComposerShape.setShapeType(QgsComposerShape.Rectangle) self.mComposerShape.setCornerRadius(30) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composershapes', 'composershape_roundedrectangle.png') myTestResult, myMessage = checker.testComposition( 'Composer shapes rounded rectangle', self.mComposition, myPath) self.mComposerShape.setCornerRadius(0) assert myTestResult == True, myMessage
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()) 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 = LOGO_ELEMENT.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 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) html_item.loadHtml() composition.exportAsPDF(output_path) return output_path
class TestQgsComposerPolyline(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) # create composition self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) # create polygon = QPolygonF() polygon.append(QPointF(0.0, 0.0)) polygon.append(QPointF(100.0, 0.0)) polygon.append(QPointF(200.0, 100.0)) polygon.append(QPointF(100.0, 200.0)) self.mComposerPolyline = QgsComposerPolyline(polygon, self.mComposition) self.mComposition.addComposerPolyline(self.mComposerPolyline) # style props = {} props["color"] = "0,0,0,255" props["width"] = "10.0" props["capstyle"] = "square" style = QgsLineSymbol.createSimple(props) self.mComposerPolyline.setPolylineStyleSymbol(style) def testDisplayName(self): """Test if displayName is valid""" self.assertEqual(self.mComposerPolyline.displayName(), "<polyline>") def testType(self): """Test if type is valid""" self.assertEqual(self.mComposerPolyline.type(), QgsComposerItem.ComposerPolyline) def testDefaultStyle(self): """Test polygon rendering with default style.""" self.mComposerPolyline.setDisplayNodes(False) checker = QgsCompositionChecker('composerpolyline_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testDisplayNodes(self): """Test displayNodes method""" self.mComposerPolyline.setDisplayNodes(True) checker = QgsCompositionChecker('composerpolyline_displaynodes', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.mComposerPolyline.setDisplayNodes(False) checker = QgsCompositionChecker('composerpolyline_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testSelectedNode(self): """Test selectedNode and deselectNode methods""" self.mComposerPolyline.setDisplayNodes(True) self.mComposerPolyline.setSelectedNode(3) checker = QgsCompositionChecker('composerpolyline_selectednode', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.mComposerPolyline.deselectNode() self.mComposerPolyline.setDisplayNodes(False) checker = QgsCompositionChecker('composerpolyline_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testRemoveNode(self): """Test removeNode method""" rc = self.mComposerPolyline.removeNode(100) self.assertEqual(rc, False) checker = QgsCompositionChecker('composerpolyline_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.removeNode(3) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolyline.nodesSize(), 3) checker = QgsCompositionChecker('composerpolyline_removednode', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testAddNode(self): """Test addNode method""" # default searching radius is 10 self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.addNode(QPointF(50.0, 10.0)) self.assertEqual(rc, False) # default searching radius is 10 self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.addNode(QPointF(50.0, 9.99)) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolyline.nodesSize(), 5) def testAddNodeCustomRadius(self): """Test addNode with custom radius""" # default searching radius is 10 self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.addNode(QPointF(50.0, 8.1), True, 8.0) self.assertEqual(rc, False) self.assertEqual(self.mComposerPolyline.nodesSize(), 4) # default searching radius is 10 rc = self.mComposerPolyline.addNode(QPointF(50.0, 7.9), True, 8.0) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolyline.nodesSize(), 5) def testAddNodeWithoutCheckingArea(self): """Test addNode without checking the maximum distance allowed""" # default searching radius is 10 self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.addNode(QPointF(50.0, 20.0)) self.assertEqual(rc, False) self.assertEqual(self.mComposerPolyline.nodesSize(), 4) # default searching radius is 10 self.assertEqual(self.mComposerPolyline.nodesSize(), 4) rc = self.mComposerPolyline.addNode(QPointF(50.0, 20.0), False) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolyline.nodesSize(), 5) checker = QgsCompositionChecker('composerpolyline_addnode', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testMoveNode(self): """Test moveNode method""" rc = self.mComposerPolyline.moveNode(30, QPointF(100.0, 300.0)) self.assertEqual(rc, False) rc = self.mComposerPolyline.moveNode(3, QPointF(100.0, 150.0)) self.assertEqual(rc, True) checker = QgsCompositionChecker('composerpolyline_movenode', self.mComposition) checker.setControlPathPrefix("composer_polyline") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testNodeAtPosition(self): """Test nodeAtPosition method""" # default searching radius is 10 rc = self.mComposerPolyline.nodeAtPosition(QPointF(100.0, 210.0)) self.assertEqual(rc, -1) # default searching radius is 10 rc = self.mComposerPolyline.nodeAtPosition(QPointF(100.0, 210.0), False) self.assertEqual(rc, 3) # default searching radius is 10 rc = self.mComposerPolyline.nodeAtPosition(QPointF(100.0, 210.0), True, 10.1) self.assertEqual(rc, 3)
class TestQgsComposerPicture(unittest.TestCase): @classmethod def setUpClass(cls): # Bring up a simple HTTP server, for remote picture tests os.chdir(unitTestDataPath() + '') handler = http.server.SimpleHTTPRequestHandler cls.httpd = socketserver.TCPServer(('localhost', 0), handler) cls.port = cls.httpd.server_address[1] cls.httpd_thread = threading.Thread(target=cls.httpd.serve_forever) cls.httpd_thread.setDaemon(True) cls.httpd_thread.start() def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) TEST_DATA_DIR = unitTestDataPath() self.pngImage = TEST_DATA_DIR + "/sample_image.png" # create composition self.mapSettings = QgsMapSettings() self.composition = QgsComposition(self.mapSettings, QgsProject.instance()) self.composition.setPaperSize(297, 210) self.composerPicture = QgsComposerPicture(self.composition) self.composerPicture.setPicturePath(self.pngImage) self.composerPicture.setSceneRect(QRectF(70, 70, 100, 100)) self.composerPicture.setFrameEnabled(True) self.composition.addComposerPicture(self.composerPicture) def testResizeZoom(self): """Test picture resize zoom mode.""" self.composerPicture.setResizeMode(QgsComposerPicture.Zoom) checker = QgsCompositionChecker('composerpicture_resize_zoom', self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() assert testResult, message @unittest.skip('test is broken for qt5/python3 - feature works') def testRemoteImage(self): """Test fetching remote picture.""" self.composerPicture.setPicturePath('http://localhost:' + str(TestQgsComposerPicture.port) + '/qgis_local_server/logo.png') checker = QgsCompositionChecker('composerpicture_remote', self.composition) checker.setControlPathPrefix("composer_picture") testResult, message = checker.testComposition() self.composerPicture.setPicturePath(self.pngImage) assert testResult, message def testGridNorth(self): """Test syncing picture to grid north""" mapSettings = QgsMapSettings() composition = QgsComposition(mapSettings, QgsProject.instance()) 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 testTrueNorth(self): """Test syncing picture to true north""" mapSettings = QgsMapSettings() composition = QgsComposition(mapSettings, QgsProject.instance()) composerMap = QgsComposerMap(composition) composerMap.setCrs(QgsCoordinateReferenceSystem.fromEpsgId(3575)) 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)
class TestQgsComposerMap(unittest.TestCase): 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) self.raster_layer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer( self.raster_layer.dataProvider(), 1, 2, 3) self.raster_layer.setRenderer(rasterRenderer) myPath = os.path.join(TEST_DATA_DIR, 'points.shp') vector_file_info = QFileInfo(myPath) self.vector_layer = QgsVectorLayer(vector_file_info.filePath(), vector_file_info.completeBaseName(), 'ogr') assert self.vector_layer.isValid() # pipe = mRasterLayer.pipe() # assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsProject.instance().addMapLayers( [self.raster_layer, self.vector_layer]) # create composition with composer map self.mComposition = QgsComposition(QgsProject.instance()) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposerMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(self.mComposerMap) def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker('composermap_overview', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker('composermap_overview_blending', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(96, -152, 160, -120) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setInverted(True) checker = QgsCompositionChecker('composermap_overview_invert', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testOverviewMapCenter(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) overviewMap.setLayers([self.raster_layer]) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(192, -288, 320, -224) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(0, -256, 256, 0) overviewMap.setNewExtent(myRectangle2) overviewMap.overview().setFrameMap(self.mComposerMap.id()) overviewMap.overview().setInverted(False) overviewMap.overview().setCentered(True) checker = QgsCompositionChecker('composermap_overview_center', self.mComposition) checker.setControlPathPrefix("composer_mapoverview") myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult, myMessage def testMapCrs(self): # create composition with composer map map_settings = QgsMapSettings() map_settings.setLayers([self.vector_layer]) composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) # check that new maps inherit project CRS QgsProject.instance().setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) map = QgsComposerMap(composition, 20, 20, 200, 100) map.setFrameEnabled(True) rectangle = QgsRectangle(-13838977, 2369660, -8672298, 6250909) map.setNewExtent(rectangle) map.setLayers([self.vector_layer]) composition.addComposerMap(map) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:3857')) self.assertEqual(map.crs().authid(), 'EPSG:3857') self.assertEqual(map.presetCrs().authid(), 'EPSG:3857') checker = QgsCompositionChecker('composermap_crs3857', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # overwrite CRS map.setCrs(QgsCoordinateReferenceSystem('EPSG:4326')) self.assertEqual(map.presetCrs().authid(), 'EPSG:4326') self.assertEqual(map.crs().authid(), 'EPSG:4326') rectangle = QgsRectangle(-124, 17, -78, 52) map.zoomToExtent(rectangle) checker = QgsCompositionChecker('composermap_crs4326', composition) checker.setControlPathPrefix("composer_map") result, message = checker.testComposition() self.assertTrue(result, message) # change back to project CRS map.setCrs(QgsCoordinateReferenceSystem()) self.assertEqual(map.crs().authid(), 'EPSG:4326') self.assertFalse(map.presetCrs().isValid()) def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXml(documentElement, doc) self.mComposition.addItemsFromXml(documentElement, doc) # test if both composer maps have different ids newMap = QgsComposerMap(self.mComposition, 0, 0, 10, 10) mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testWorldFileGeneration(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setMapRotation(30.0) self.mComposition.setGenerateWorldFile(True) self.mComposition.setReferenceMap(self.mComposerMap) p = self.mComposition.computeWorldFileParameters() pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, 2.4136013686911886, -4.179969388427311, 3342408.5663611) ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) for i in range(0, 6): assert abs(p[i] - pexpected[i]) < ptolerance[i]
class TestQgsComposerMap(TestCase): def __init__(self, methodName): """Run once on class initialisation.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') rasterFileInfo = QFileInfo(myPath) mRasterLayer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer(mRasterLayer.dataProvider(), 2, 3, 4) mRasterLayer.setRenderer(rasterRenderer) #pipe = mRasterLayer.pipe() #assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsMapLayerRegistry.instance().addMapLayers([mRasterLayer]) # create composition with composer map self.mMapRenderer = QgsMapRenderer() layerStringList = [] layerStringList.append(mRasterLayer.id()) self.mMapRenderer.setLayerSet(layerStringList) self.mMapRenderer.setProjectionsEnabled(False) self.mComposition = QgsComposition(self.mMapRenderer) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mComposerMap) def testGrid(self): """Test that we can create a grid for a map.""" myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) self.mComposerMap.setShowGridAnnotation(True) self.mComposerMap.setGridPenWidth(0.5) self.mComposerMap.setGridPenColor(QColor(0, 255, 0)) self.mComposerMap.setGridAnnotationFont( QgsFontUtils.getStandardTestFont()) self.mComposerMap.setGridAnnotationPrecision(0) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Left) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Top) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Bottom) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Bottom) self.mComposerMap.setAnnotationFontColor(QColor(255, 0, 0, 150)) self.mComposerMap.setGridBlendMode(QPainter.CompositionMode_Overlay) checker = QgsCompositionChecker('composermap_grid', self.mComposition) myTestResult, myMessage = checker.testComposition() self.mComposerMap.setGridEnabled(False) self.mComposerMap.setShowGridAnnotation(False) assert myTestResult == True, myMessage def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker('composermap_overview', self.mComposition) myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker('composermap_overview_blending', self.mComposition) myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(True) checker = QgsCompositionChecker('composermap_overview_invert', self.mComposition) myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapCenter(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375 + 5000, 3341423.125, 789262.375 + 5000, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(False) overviewMap.setOverviewCentered(True) checker = QgsCompositionChecker('composermap_overview_center', self.mComposition) myTestResult, myMessage = checker.testComposition() self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage # Fails because addItemsFromXML has been commented out in sip @expectedFailure def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXML(documentElement, doc) self.mComposition.addItemsFromXML(documentElement, doc, 0, False) #test if both composer maps have different ids newMap = QgsComposerMap() mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testZebraStyle(self): self.mComposerMap.setGridFrameStyle(QgsComposerMap.Zebra) myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) self.mComposerMap.setGridFrameWidth(10) self.mComposerMap.setGridFramePenSize(1) self.mComposerMap.setGridPenWidth(0.5) self.mComposerMap.setGridFramePenColor(QColor(255, 100, 0, 200)) self.mComposerMap.setGridFrameFillColor1(QColor(50, 90, 50, 100)) self.mComposerMap.setGridFrameFillColor2(QColor(200, 220, 100, 60)) checker = QgsCompositionChecker('composermap_zebrastyle', self.mComposition) myTestResult, myMessage = checker.testComposition() assert myTestResult == True, myMessage def testWorldFileGeneration(self): myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setMapRotation(30.0) self.mComposition.setGenerateWorldFile(True) self.mComposition.setWorldFileMap(self.mComposerMap) p = self.mComposition.computeWorldFileParameters() pexpected = (4.180480199790922, 2.4133064516129026, 779443.7612381146, 2.4136013686911886, -4.179969388427311, 3342408.5663611) ptolerance = (0.001, 0.001, 1, 0.001, 0.001, 1e+03) for i in range(0, 6): assert abs(p[i] - pexpected[i]) < ptolerance[i]
class TestQgsComposerPolygon(unittest.TestCase): def __init__(self, methodName): """Run once on class initialization.""" unittest.TestCase.__init__(self, methodName) self.mapSettings = QgsMapSettings() # create composition self.mComposition = QgsComposition(self.mapSettings) self.mComposition.setPaperSize(297, 210) # create polygon = QPolygonF() polygon.append(QPointF(0.0, 0.0)) polygon.append(QPointF(100.0, 0.0)) polygon.append(QPointF(200.0, 100.0)) polygon.append(QPointF(100.0, 200.0)) self.mComposerPolygon = QgsComposerPolygon(polygon, self.mComposition) self.mComposition.addComposerPolygon(self.mComposerPolygon) # style props = {} props["color"] = "green" props["style"] = "solid" props["style_border"] = "solid" props["color_border"] = "black" props["width_border"] = "10.0" props["joinstyle"] = "miter" style = QgsFillSymbol.createSimple(props) self.mComposerPolygon.setPolygonStyleSymbol(style) def testDisplayName(self): """Test if displayName is valid""" self.assertEqual(self.mComposerPolygon.displayName(), "<polygon>") def testType(self): """Test if type is valid""" self.assertEqual( self.mComposerPolygon.type(), QgsComposerItem.ComposerPolygon) def testDefaultStyle(self): """Test polygon rendering with default style.""" self.mComposerPolygon.setDisplayNodes(False) checker = QgsCompositionChecker( 'composerpolygon_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testDisplayNodes(self): """Test displayNodes method""" self.mComposerPolygon.setDisplayNodes(True) checker = QgsCompositionChecker( 'composerpolygon_displaynodes', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.mComposerPolygon.setDisplayNodes(False) checker = QgsCompositionChecker( 'composerpolygon_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testSelectedNode(self): """Test selectedNode and unselectNode methods""" self.mComposerPolygon.setDisplayNodes(True) self.mComposerPolygon.setSelectedNode(3) checker = QgsCompositionChecker( 'composerpolygon_selectednode', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.mComposerPolygon.unselectNode() self.mComposerPolygon.setDisplayNodes(False) checker = QgsCompositionChecker( 'composerpolygon_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testRemoveNode(self): """Test removeNode method""" rc = self.mComposerPolygon.removeNode(100) self.assertEqual(rc, False) checker = QgsCompositionChecker( 'composerpolygon_defaultstyle', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage self.assertEqual(self.mComposerPolygon.nodesSize(), 4) def testAddNode(self): """Test addNode method""" # default searching radius is 10 self.assertEqual(self.mComposerPolygon.nodesSize(), 4) rc = self.mComposerPolygon.addNode(QPointF(50.0, 10.0)) self.assertEqual(rc, False) # default searching radius is 10 self.assertEqual(self.mComposerPolygon.nodesSize(), 4) rc = self.mComposerPolygon.addNode(QPointF(50.0, 9.99)) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolygon.nodesSize(), 5) def testAddNodeCustomRadius(self): """Test addNode with custom radius""" # default searching radius is 10 self.assertEqual(self.mComposerPolygon.nodesSize(), 4) rc = self.mComposerPolygon.addNode(QPointF(50.0, 8.1), True, 8.0) self.assertEqual(rc, False) self.assertEqual(self.mComposerPolygon.nodesSize(), 4) # default searching radius is 10 rc = self.mComposerPolygon.addNode(QPointF(50.0, 7.9), True, 8.0) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolygon.nodesSize(), 5) def testAddNodeWithoutCheckingArea(self): """Test addNode without checking the maximum distance allowed""" # default searching radius is 10 self.assertEqual(self.mComposerPolygon.nodesSize(), 4) rc = self.mComposerPolygon.addNode(QPointF(50.0, 20.0)) self.assertEqual(rc, False) self.assertEqual(self.mComposerPolygon.nodesSize(), 4) # default searching radius is 10 self.assertEqual(self.mComposerPolygon.nodesSize(), 4) rc = self.mComposerPolygon.addNode(QPointF(50.0, 20.0), False) self.assertEqual(rc, True) self.assertEqual(self.mComposerPolygon.nodesSize(), 5) checker = QgsCompositionChecker( 'composerpolygon_addnode', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testMoveNode(self): """Test moveNode method""" rc = self.mComposerPolygon.moveNode(30, QPointF(100.0, 300.0)) self.assertEqual(rc, False) rc = self.mComposerPolygon.moveNode(3, QPointF(100.0, 150.0)) self.assertEqual(rc, True) checker = QgsCompositionChecker( 'composerpolygon_movenode', self.mComposition) checker.setControlPathPrefix("composer_polygon") myTestResult, myMessage = checker.testComposition() assert myTestResult, myMessage def testNodeAtPosition(self): """Test nodeAtPosition method""" # default searching radius is 10 rc = self.mComposerPolygon.nodeAtPosition(QPointF(100.0, 210.0)) self.assertEqual(rc, -1) # default searching radius is 10 rc = self.mComposerPolygon.nodeAtPosition( QPointF(100.0, 210.0), False) self.assertEqual(rc, 3) # default searching radius is 10 rc = self.mComposerPolygon.nodeAtPosition( QPointF(100.0, 210.0), True, 10.1) self.assertEqual(rc, 3)
class TestComposerBase(TestQgsPalLabeling): layer = None """:type: QgsVectorLayer""" @classmethod def setUpClass(cls): if not cls._BaseSetup: TestQgsPalLabeling.setUpClass() # the blue background (set via layer style) to match renderchecker's TestQgsPalLabeling.loadFeatureLayer('background', True) cls._TestKind = 0 # OutputKind.(Img|Svg|Pdf) @classmethod def tearDownClass(cls): """Run after all tests""" TestQgsPalLabeling.tearDownClass() cls.removeMapLayer(cls.layer) cls.layer = None def setUp(self): """Run before each test.""" super(TestComposerBase, self).setUp() self._TestImage = '' # ensure per test map settings stay encapsulated self._TestMapSettings = self.cloneMapSettings(self._MapSettings) self._Mismatch = 0 self._ColorTol = 0 self._Mismatches.clear() self._ColorTols.clear() def _set_up_composition(self, width, height, dpi): # set up composition and add map self._c = QgsComposition(self._TestMapSettings) """:type: QgsComposition""" # self._c.setUseAdvancedEffects(False) self._c.setPrintResolution(dpi) # 600 x 400 px = 211.67 x 141.11 mm @ 72 dpi paperw = width * 25.4 / dpi paperh = height * 25.4 / dpi self._c.setPaperSize(paperw, paperh) # NOTE: do not use QgsComposerMap(self._c, 0, 0, paperw, paperh) since # it only takes integers as parameters and the composition will grow # larger based upon union of item scene rectangles and a slight buffer # see end of QgsComposition::compositionBounds() # add map as small graphics item first, then set its scene QRectF later self._cmap = QgsComposerMap(self._c, 10, 10, 10, 10) """:type: QgsComposerMap""" self._cmap.setPreviewMode(QgsComposerMap.Render) self._cmap.setFrameEnabled(False) self._c.addComposerMap(self._cmap) # now expand map to fill page and set its extent self._cmap.setSceneRect(QRectF(0, 0, paperw, paperw)) self._cmap.setNewExtent(self.aoiExtent()) # self._cmap.updateCachedImage() self._c.setPlotStyle(QgsComposition.Print) # noinspection PyUnusedLocal def _get_composer_image(self, width, height, dpi): image = QImage(QSize(width, height), self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing)) self._c.renderPage(p, 0) p.end() # image = self._c.printPageAsRaster(0) # """:type: QImage""" if image.isNull(): return False, '' filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' return res, filepath def _get_composer_svg_image(self, width, height, dpi): # from qgscomposer.cpp, QgsComposer::on_mActionExportAsSVG_triggered, # near end of function svgpath = getTempfilePath('svg') temp_size = os.path.getsize(svgpath) svg_g = QSvgGenerator() # noinspection PyArgumentList svg_g.setTitle(QgsProject.instance().title()) svg_g.setFileName(svgpath) svg_g.setSize(QSize(width, height)) svg_g.setViewBox(QRect(0, 0, width, height)) svg_g.setResolution(dpi) sp = QPainter(svg_g) self._c.renderPage(sp, 0) sp.end() if temp_size == os.path.getsize(svgpath): return False, '' image = QImage(width, height, self._TestMapSettings.outputImageFormat()) image.fill(QColor(152, 219, 249).rgb()) image.setDotsPerMeterX(dpi / 25.4 * 1000) image.setDotsPerMeterY(dpi / 25.4 * 1000) svgr = QSvgRenderer(svgpath) p = QPainter(image) p.setRenderHint( QPainter.Antialiasing, self._TestMapSettings.testFlag(QgsMapSettings.Antialiasing)) p.setRenderHint(QPainter.TextAntialiasing) svgr.render(p) p.end() filepath = getTempfilePath('png') res = image.save(filepath, 'png') if not res: os.unlink(filepath) filepath = '' # TODO: remove .svg file as well? return res, filepath def _get_composer_pdf_image(self, width, height, dpi): pdfpath = getTempfilePath('pdf') temp_size = os.path.getsize(pdfpath) p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(pdfpath) p.setPaperSize(QSizeF(self._c.paperWidth(), self._c.paperHeight()), QPrinter.Millimeter) p.setFullPage(True) p.setColorMode(QPrinter.Color) p.setResolution(self._c.printResolution()) pdf_p = QPainter(p) # page_mm = p.pageRect(QPrinter.Millimeter) # page_px = p.pageRect(QPrinter.DevicePixel) # self._c.render(pdf_p, page_px, page_mm) self._c.renderPage(pdf_p, 0) pdf_p.end() if temp_size == os.path.getsize(pdfpath): return False, '' filepath = getTempfilePath('png') # Poppler (pdftocairo or pdftoppm): # PDFUTIL -png -singlefile -r 72 -x 0 -y 0 -W 420 -H 280 in.pdf pngbase # muPDF (mudraw): # PDFUTIL -c rgb[a] -r 72 -w 420 -h 280 -o out.png in.pdf if PDFUTIL.strip().endswith('pdftocairo'): filebase = os.path.join( os.path.dirname(filepath), os.path.splitext(os.path.basename(filepath))[0]) call = [ PDFUTIL, '-png', '-singlefile', '-r', str(dpi), '-x', '0', '-y', '0', '-W', str(width), '-H', str(height), pdfpath, filebase ] elif PDFUTIL.strip().endswith('mudraw'): call = [ PDFUTIL, '-c', 'rgba', '-r', str(dpi), '-w', str(width), '-h', str(height), # '-b', '8', '-o', filepath, pdfpath ] else: return False, '' qDebug("_get_composer_pdf_image call: {0}".format(' '.join(call))) res = False try: subprocess.check_call(call) res = True except subprocess.CalledProcessError as e: qDebug("_get_composer_pdf_image failed!\n" "cmd: {0}\n" "returncode: {1}\n" "message: {2}".format(e.cmd, e.returncode, e.message)) if not res: os.unlink(filepath) filepath = '' return res, filepath def get_composer_output(self, kind): ms = self._TestMapSettings osize = ms.outputSize() width, height, dpi = osize.width(), osize.height(), ms.outputDpi() self._set_up_composition(width, height, dpi) if kind == OutputKind.Svg: return self._get_composer_svg_image(width, height, dpi) elif kind == OutputKind.Pdf: return self._get_composer_pdf_image(width, height, dpi) else: # OutputKind.Img return self._get_composer_image(width, height, dpi) # noinspection PyUnusedLocal def checkTest(self, **kwargs): self.lyr.writeToLayer(self.layer) ms = self._MapSettings # class settings settings_type = 'Class' if self._TestMapSettings is not None: ms = self._TestMapSettings # per test settings settings_type = 'Test' if 'PAL_VERBOSE' in os.environ: qDebug('MapSettings type: {0}'.format(settings_type)) qDebug(mapSettingsString(ms)) res_m, self._TestImage = self.get_composer_output(self._TestKind) self.assertTrue(res_m, 'Failed to retrieve/save output from composer') self.saveControlImage(self._TestImage) mismatch = 0 if 'PAL_NO_MISMATCH' not in os.environ: # some mismatch expected mismatch = self._Mismatch if self._Mismatch else 20 if self._TestGroup in self._Mismatches: mismatch = self._Mismatches[self._TestGroup] colortol = 0 if 'PAL_NO_COLORTOL' not in os.environ: colortol = self._ColorTol if self._ColorTol else 0 if self._TestGroup in self._ColorTols: colortol = self._ColorTols[self._TestGroup] self.assertTrue(*self.renderCheck( mismatch=mismatch, colortol=colortol, imgpath=self._TestImage))
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()) 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 = LOGO_ELEMENT.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 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) html_item.loadHtml() composition.exportAsPDF(output_path) return output_path
class TestQgsComposerMap(TestCase): def __init__(self, methodName): """Run once on class initialisation.""" unittest.TestCase.__init__(self, methodName) myPath = os.path.join(TEST_DATA_DIR, 'landsat.tif') rasterFileInfo = QFileInfo(myPath) mRasterLayer = QgsRasterLayer(rasterFileInfo.filePath(), rasterFileInfo.completeBaseName()) rasterRenderer = QgsMultiBandColorRenderer( mRasterLayer.dataProvider(), 2, 3, 4) mRasterLayer.setRenderer(rasterRenderer) #pipe = mRasterLayer.pipe() #assert pipe.set(rasterRenderer), 'Cannot set pipe renderer' QgsMapLayerRegistry.instance().addMapLayers([mRasterLayer]) # create composition with composer map self.mMapRenderer = QgsMapRenderer() layerStringList = QStringList() layerStringList.append(mRasterLayer.id()) self.mMapRenderer.setLayerSet(layerStringList) self.mMapRenderer.setProjectionsEnabled(False) self.mComposition = QgsComposition(self.mMapRenderer) self.mComposition.setPaperSize(297, 210) self.mComposerMap = QgsComposerMap(self.mComposition, 20, 20, 200, 100) self.mComposerMap.setFrameEnabled(True) self.mComposition.addComposerMap(self.mComposerMap) def testGrid(self): """Test that we can create a grid for a map.""" myRectangle = QgsRectangle(781662.375, 3339523.125, 793062.375, 3345223.125) self.mComposerMap.setNewExtent(myRectangle) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) self.mComposerMap.setShowGridAnnotation(True) self.mComposerMap.setGridPenWidth(0.5) self.mComposerMap.setGridAnnotationPrecision(0) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Left) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationPosition(QgsComposerMap.Disabled, QgsComposerMap.Top) self.mComposerMap.setGridAnnotationPosition( QgsComposerMap.OutsideMapFrame, QgsComposerMap.Bottom) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Right) self.mComposerMap.setGridAnnotationDirection(QgsComposerMap.Horizontal, QgsComposerMap.Bottom) checker = QgsCompositionChecker() myPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_grid.png') myTestResult, myMessage = checker.testComposition('Composer map grid', self.mComposition, myPath) self.mComposerMap.setGridEnabled(False) self.mComposerMap.setShowGridAnnotation(False) assert myTestResult == True, myMessage def testOverviewMap(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapBlend(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewBlendMode(QPainter.CompositionMode_Multiply) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview_blend.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview blending', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage def testOverviewMapInvert(self): overviewMap = QgsComposerMap(self.mComposition, 20, 130, 70, 70) overviewMap.setFrameEnabled(True) self.mComposition.addComposerMap(overviewMap) # zoom in myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent(myRectangle) myRectangle2 = QgsRectangle(781662.375, 3339523.125, 793062.375, 3350923.125) overviewMap.setNewExtent(myRectangle2) overviewMap.setOverviewFrameMap(self.mComposerMap.id()) overviewMap.setOverviewInverted(True) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_landsat_overview_invert.png') myTestResult, myMessage = checker.testComposition( 'Composer map overview inverted', self.mComposition, myPngPath) self.mComposition.removeComposerItem(overviewMap) assert myTestResult == True, myMessage # Fails because addItemsFromXML has been commented out in sip @expectedFailure def testuniqueId(self): doc = QDomDocument() documentElement = doc.createElement('ComposerItemClipboard') self.mComposition.writeXML(documentElement, doc) self.mComposition.addItemsFromXML(documentElement, doc, 0, False) #test if both composer maps have different ids newMap = QgsComposerMap() mapList = self.mComposition.composerMapItems() for mapIt in mapList: if mapIt != self.mComposerMap: newMap = mapIt break oldId = self.mComposerMap.id() newId = newMap.id() self.mComposition.removeComposerItem(newMap) myMessage = 'old: %s new: %s' % (oldId, newId) assert oldId != newId, myMessage def testZebraStyle(self): self.mComposerMap.setGridFrameStyle(QgsComposerMap.Zebra) myRectangle = QgsRectangle(785462.375, 3341423.125, 789262.375, 3343323.125) self.mComposerMap.setNewExtent( myRectangle ) self.mComposerMap.setGridEnabled(True) self.mComposerMap.setGridIntervalX(2000) self.mComposerMap.setGridIntervalY(2000) checker = QgsCompositionChecker() myPngPath = os.path.join(TEST_DATA_DIR, 'control_images', 'expected_composermap', 'composermap_zebra_style.png') testResult, myMessage = checker.testComposition('Composer map zebra', self.mComposition, myPngPath) assert testResult == True, myMessage
class TestQgsComposerHtml(unittest.TestCase): 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 tearDown(self): """Run after each test.""" print("Tear down") def htmlUrl(self): """Helper to get the url of the html doc.""" myPath = os.path.join(TEST_DATA_DIR, "test_html.html") myUrl = QUrl("file:///" + myPath) return myUrl def testTable(self): """Test we can render a html table in a single frame.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 0, 0, 100, 200) htmlFrame.setFrameEnabled(True) composerHtml.addFrame(htmlFrame) composerHtml.setUrl(self.htmlUrl()) checker = QgsCompositionChecker('composerhtml_table', self.mComposition) checker.setControlPathPrefix("composer_html") myTestResult, myMessage = checker.testComposition() qDebug(myMessage) self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testTableMultiFrame(self): """Test we can render to multiframes.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 50) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(False) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print("Checking page 1") myPage = 0 checker1 = QgsCompositionChecker('composerhtml_multiframe1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage) assert myTestResult, myMessage print("Checking page 2") myPage = 1 checker2 = QgsCompositionChecker('composerhtml_multiframe2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage def testHtmlSmartBreaks(self): """Test rendering to multiframes with smart breaks.""" composerHtml = QgsComposerHtml(self.mComposition, False) htmlFrame = QgsComposerFrame(self.mComposition, composerHtml, 10, 10, 100, 52) composerHtml.addFrame(htmlFrame) composerHtml.setResizeMode(QgsComposerMultiFrame.RepeatUntilFinished) composerHtml.setUseSmartBreaks(True) composerHtml.setUrl(self.htmlUrl()) composerHtml.frame(0).setFrameEnabled(True) print("Checking page 1") myPage = 0 checker1 = QgsCompositionChecker('composerhtml_smartbreaks1', self.mComposition) checker1.setControlPathPrefix("composer_html") myTestResult, myMessage = checker1.testComposition(myPage, 200) assert myTestResult, myMessage print("Checking page 2") myPage = 1 checker2 = QgsCompositionChecker('composerhtml_smartbreaks2', self.mComposition) checker2.setControlPathPrefix("composer_html") myTestResult, myMessage = checker2.testComposition(myPage, 200) assert myTestResult, myMessage self.mComposition.removeMultiFrame(composerHtml) composerHtml = None assert myTestResult, myMessage