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
Exemple #4
0
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)
Exemple #8
0
    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())
Exemple #9
0
    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)
Exemple #11
0
    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()])
Exemple #13
0
    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()])
Exemple #15
0
    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 )
Exemple #16
0
    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()])
Exemple #18
0
    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
Exemple #19
0
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
Exemple #20
0
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]
Exemple #21
0
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
Exemple #22
0
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())
Exemple #24
0
    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
Exemple #25
0
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()
Exemple #29
0
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
Exemple #31
0
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)
Exemple #32
0
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))
Exemple #33
0
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()
Exemple #37
0
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
Exemple #39
0
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
Exemple #40
0
    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)
Exemple #43
0
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]
Exemple #44
0
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]
Exemple #45
0
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))
Exemple #47
0
    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
Exemple #49
0
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