Пример #1
0
class TestQgsRulebasedRenderer(unittest.TestCase):

    def setUp(self):
        myShpFile = os.path.join(TEST_DATA_DIR, 'rectangles.shp')
        layer = QgsVectorLayer(myShpFile, 'Points', 'ogr')
        QgsProject.instance().addMapLayer(layer)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.r1)
        rootrule.appendChild(self.r2)
        rootrule.appendChild(self.r3)

        layer.setRenderer(QgsRuleBasedRenderer(rootrule))
        self.mapsettings = QgsMapSettings()
        self.mapsettings.setOutputSize(QSize(400, 400))
        self.mapsettings.setOutputDpi(96)
        self.mapsettings.setExtent(QgsRectangle(-163, 22, -70, 52))

        rendered_layers = [layer]
        self.mapsettings.setLayers(rendered_layers)

    def testElse(self):
        # Setup rendering check
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_else')
        result = renderchecker.runTest('rulebased_else')

        assert result

    def testDisabledElse(self):
        # Disable a rule and assert that it's hidden not rendered with else
        self.r2.setActive(False)

        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_disabled_else')
        result = renderchecker.runTest('rulebased_disabled_else')

        assert result

    def testWillRenderFeature(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0) # 'id' = 1
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        renderer.rootRule().children()[0].setActive(False)
        renderer.rootRule().children()[1].setActive(True)
        renderer.rootRule().children()[2].setActive(True)

        renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        renderer.rootRule().children()[0].setActive(True)
        assert rendered == False

        renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        assert rendered == True

    def testWillRenderFeatureNestedElse(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0) # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.rx1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        self.rx3.appendChild(self.rx1)

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Reunder with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

        # Reunder with else rule where else is deactivated
        renderer.rootRule().children()[1].setActive(False)
        renderer.startRender(ctx, vl.fields())
        self.assertFalse(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

    def testFeatureCount(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(2) # 'id' = 3 => ELSE
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        counter = vl.countSymbolFeatures()
        counter.waitForFinished()

        renderer.startRender(ctx, vl.fields())

        elseRule = None
        for rule in renderer.rootRule().children():
            if rule.filterExpression() == 'ELSE':
                elseRule = rule

        assert elseRule != None

        cnt = counter.featureCount(elseRule.ruleKey())
        assert cnt == 1

    def testRefineWithCategories(self):
        # Test refining rule with categories (refs #10815)

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r2, c)
        assert self.r2.children()[0].filterExpression() == '"id" = 1'
        assert self.r2.children()[1].filterExpression() == '"id" = 2'

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r1, c)
        assert self.r1.children()[0].filterExpression() == 'id + 1 = 1'
        assert self.r1.children()[1].filterExpression() == 'id + 1 = 2'

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r3, c)
        assert self.r3.children()[0].filterExpression() == '"id" = 1'
        assert self.r3.children()[1].filterExpression() == '"id" = 2'

    def testRefineWithRanges(self):
        # Test refining rule with ranges (refs #10815)

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r2, g)
        assert self.r2.children()[0].filterExpression() == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r2.children()[1].filterExpression() == '"id" > 1.0000 AND "id" <= 2.0000'

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r1, g)
        assert self.r1.children()[0].filterExpression() == '(id / 2) >= 0.0000 AND (id / 2) <= 1.0000'
        assert self.r1.children()[1].filterExpression() == '(id / 2) > 1.0000 AND (id / 2) <= 2.0000'

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r3, g)
        assert self.r3.children()[0].filterExpression() == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r3.children()[1].filterExpression() == '"id" > 1.0000 AND "id" <= 2.0000'

    def testConvertFromCategorisedRenderer(self):
        # Test converting categorised renderer to rule based

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        cats.append(QgsRendererCategory('a\'b', QgsMarkerSymbol(), "id a'b"))
        cats.append(QgsRendererCategory('a\nb', QgsMarkerSymbol(), "id a\\nb"))
        cats.append(QgsRendererCategory('a\\b', QgsMarkerSymbol(), "id a\\\\b"))
        cats.append(QgsRendererCategory('a\tb', QgsMarkerSymbol(), "id a\\tb"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(), '"id" = \'a\'\'b\'')
        self.assertEqual(r.rootRule().children()[3].filterExpression(), '"id" = \'a\\nb\'')
        self.assertEqual(r.rootRule().children()[4].filterExpression(), '"id" = \'a\\\\b\'')
        self.assertEqual(r.rootRule().children()[5].filterExpression(), '"id" = \'a\\tb\'')

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), 'id + 1 = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), 'id + 1 = 2')

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')

    def testConvertFromGraduatedRenderer(self):
        # Test converting graduated renderer to rule based

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '(id / 2) >= 0.000000 AND (id / 2) <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '(id / 2) > 1.000000 AND (id / 2) <= 2.000000')

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')
Пример #2
0
class TestQgsRulebasedRenderer(unittest.TestCase):

    def setUp(self):
        myShpFile = os.path.join(TEST_DATA_DIR, 'rectangles.shp')
        layer = QgsVectorLayer(myShpFile, 'Points', 'ogr')
        QgsProject.instance().addMapLayer(layer)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.r1)
        rootrule.appendChild(self.r2)
        rootrule.appendChild(self.r3)

        layer.setRenderer(QgsRuleBasedRenderer(rootrule))
        self.mapsettings = QgsMapSettings()
        self.mapsettings.setOutputSize(QSize(400, 400))
        self.mapsettings.setOutputDpi(96)
        self.mapsettings.setExtent(QgsRectangle(-163, 22, -70, 52))

        rendered_layers = [layer]
        self.mapsettings.setLayers(rendered_layers)

    def testElse(self):
        # Setup rendering check
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_else')
        result = renderchecker.runTest('rulebased_else')

        assert result

    def testDisabledElse(self):
        # Disable a rule and assert that it's hidden not rendered with else
        self.r2.setActive(False)

        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_disabled_else')
        result = renderchecker.runTest('rulebased_disabled_else')

        assert result

    def testWillRenderFeature(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0) # 'id' = 1
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        renderer.rootRule().children()[0].setActive(False)
        renderer.rootRule().children()[1].setActive(True)
        renderer.rootRule().children()[2].setActive(True)

        renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        renderer.rootRule().children()[0].setActive(True)
        assert rendered == False

        renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        assert rendered == True

    def testWillRenderFeatureNestedElse(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0) # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.rx1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        self.rx3.appendChild(self.rx1)

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Reunder with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

        # Reunder with else rule where else is deactivated
        renderer.rootRule().children()[1].setActive(False)
        renderer.startRender(ctx, vl.fields())
        self.assertFalse(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

    def testFeatureCount(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(2) # 'id' = 3 => ELSE
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        counter = vl.countSymbolFeatures()
        counter.waitForFinished()

        renderer.startRender(ctx, vl.fields())

        elseRule = None
        for rule in renderer.rootRule().children():
            if rule.filterExpression() == 'ELSE':
                elseRule = rule

        assert elseRule != None

        cnt = counter.featureCount(elseRule.ruleKey())
        assert cnt == 1

    def testRefineWithCategories(self):
        # Test refining rule with categories (refs #10815)

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r2, c)
        assert self.r2.children()[0].filterExpression() == '"id" = 1'
        assert self.r2.children()[1].filterExpression() == '"id" = 2'

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r1, c)
        assert self.r1.children()[0].filterExpression() == 'id + 1 = 1'
        assert self.r1.children()[1].filterExpression() == 'id + 1 = 2'

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r3, c)
        assert self.r3.children()[0].filterExpression() == '"id" = 1'
        assert self.r3.children()[1].filterExpression() == '"id" = 2'

    def testRefineWithRanges(self):
        # Test refining rule with ranges (refs #10815)

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r2, g)
        assert self.r2.children()[0].filterExpression() == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r2.children()[1].filterExpression() == '"id" > 1.0000 AND "id" <= 2.0000'

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r1, g)
        assert self.r1.children()[0].filterExpression() == '(id / 2) >= 0.0000 AND (id / 2) <= 1.0000'
        assert self.r1.children()[1].filterExpression() == '(id / 2) > 1.0000 AND (id / 2) <= 2.0000'

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r3, g)
        assert self.r3.children()[0].filterExpression() == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r3.children()[1].filterExpression() == '"id" > 1.0000 AND "id" <= 2.0000'

    def testConvertFromCategorisedRenderer(self):
        # Test converting categorised renderer to rule based

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        cats.append(QgsRendererCategory('a\'b', QgsMarkerSymbol(), "id a'b"))
        cats.append(QgsRendererCategory('a\nb', QgsMarkerSymbol(), "id a\\nb"))
        cats.append(QgsRendererCategory('a\\b', QgsMarkerSymbol(), "id a\\\\b"))
        cats.append(QgsRendererCategory('a\tb', QgsMarkerSymbol(), "id a\\tb"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(), '"id" = \'a\'\'b\'')
        self.assertEqual(r.rootRule().children()[3].filterExpression(), '"id" = \'a\\nb\'')
        self.assertEqual(r.rootRule().children()[4].filterExpression(), '"id" = \'a\\\\b\'')
        self.assertEqual(r.rootRule().children()[5].filterExpression(), '"id" = \'a\\tb\'')

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), 'id + 1 = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), 'id + 1 = 2')

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')

    def testConvertFromGraduatedRenderer(self):
        # Test converting graduated renderer to rule based

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '(id / 2) >= 0.000000 AND (id / 2) <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '(id / 2) > 1.000000 AND (id / 2) <= 2.000000')

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')
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())
Пример #4
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(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())
Пример #5
0
class TestQgsRulebasedRenderer(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        # Super ugly hack to make sure python does not clean up our mapsetting objects
        # this might lead to occasional crashes on travis
        cls.mapsettings_archive = list()

    def setUp(self):
        myShpFile = os.path.join(TEST_DATA_DIR, 'rectangles.shp')
        layer = QgsVectorLayer(myShpFile, 'Rectangles', 'ogr')
        QgsProject.instance().addMapLayer(layer)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.r1)
        rootrule.appendChild(self.r2)
        rootrule.appendChild(self.r3)

        layer.setRenderer(QgsRuleBasedRenderer(rootrule))
        self.mapsettings = QgsMapSettings()
        self.mapsettings.setOutputSize(QSize(400, 400))
        self.mapsettings.setOutputDpi(96)
        self.mapsettings.setExtent(QgsRectangle(-163, 22, -70, 52))

        rendered_layers = [layer]
        self.mapsettings.setLayers(rendered_layers)
        self.mapsettings_archive.append(self.mapsettings)

    def testElse(self):
        # Setup rendering check
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_else')
        self.assertTrue(renderchecker.runTest('rulebased_else'))

    def testDisabledElse(self):
        # Disable a rule and assert that it's hidden not rendered with else
        self.r2.setActive(False)

        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_disabled_else')
        self.assertTrue(renderchecker.runTest('rulebased_disabled_else'))

    def testWillRenderFeature(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        renderer.rootRule().children()[0].setActive(False)
        renderer.rootRule().children()[1].setActive(True)
        renderer.rootRule().children()[2].setActive(True)

        renderer.startRender(ctx, vl.fields())  # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        renderer.rootRule().children()[0].setActive(True)
        self.assertFalse(rendered)

        renderer.startRender(ctx, vl.fields())  # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        self.assertTrue(rendered)

    def testWillRenderFeatureNestedElse(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.rx1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        self.rx3.appendChild(self.rx1)

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Render with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

        # Render with else rule where else is deactivated
        renderer.rootRule().children()[1].setActive(False)
        renderer.startRender(ctx, vl.fields())
        self.assertFalse(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

    def testFeatureCount(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(2)  # 'id' = 3 => ELSE
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        counter = vl.countSymbolFeatures()
        counter.waitForFinished()

        renderer.startRender(ctx, vl.fields())

        elseRule = None
        for rule in renderer.rootRule().children():
            if rule.filterExpression() == 'ELSE':
                elseRule = rule

        self.assertIsNotNone(elseRule)

        cnt = counter.featureCount(elseRule.ruleKey())
        self.assertEqual(cnt, 1)

    def testRefineWithCategories(self):
        # Test refining rule with categories (refs #10815)

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), ''))
        cats.append(QgsRendererCategory(None, QgsMarkerSymbol(), ''))
        c = QgsCategorizedSymbolRenderer("id", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r2, c)
        self.assertEqual(self.r2.children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(self.r2.children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(self.r2.children()[0].label(), 'id 1')
        self.assertEqual(self.r2.children()[1].label(), '2')
        self.assertEqual(self.r2.children()[2].label(), '')

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r1, c)
        self.assertEqual(self.r1.children()[0].filterExpression(), 'id + 1 = 1')
        self.assertEqual(self.r1.children()[1].filterExpression(), 'id + 1 = 2')
        self.assertEqual(self.r1.children()[0].label(), 'result 1')
        self.assertEqual(self.r1.children()[1].label(), 'result 2')

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r3, c)
        self.assertEqual(self.r3.children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(self.r3.children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(self.r3.children()[0].label(), 'result 1')
        self.assertEqual(self.r3.children()[1].label(), 'result 2')

    def testRefineWithRanges(self):
        # Test refining rule with ranges (refs #10815)

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r2, g)
        self.assertEqual(self.r2.children()[0].filterExpression(), '"id" >= 0.0000 AND "id" <= 1.0000')
        self.assertEqual(self.r2.children()[1].filterExpression(), '"id" > 1.0000 AND "id" <= 2.0000')

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r1, g)
        self.assertEqual(self.r1.children()[0].filterExpression(), '(id / 2) >= 0.0000 AND (id / 2) <= 1.0000')
        self.assertEqual(self.r1.children()[1].filterExpression(), '(id / 2) > 1.0000 AND (id / 2) <= 2.0000')

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r3, g)
        self.assertEqual(self.r3.children()[0].filterExpression(), '"id" >= 0.0000 AND "id" <= 1.0000')
        self.assertEqual(self.r3.children()[1].filterExpression(), '"id" > 1.0000 AND "id" <= 2.0000')

    def testConvertFromCategorisedRenderer(self):
        # Test converting categorised renderer to rule based

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        cats.append(QgsRendererCategory('a\'b', QgsMarkerSymbol(), "id a'b"))
        cats.append(QgsRendererCategory('a\nb', QgsMarkerSymbol(), "id a\\nb"))
        cats.append(QgsRendererCategory('a\\b', QgsMarkerSymbol(), "id a\\\\b"))
        cats.append(QgsRendererCategory('a\tb', QgsMarkerSymbol(), "id a\\tb"))
        cats.append(QgsRendererCategory(['c', 'd'], QgsMarkerSymbol(), "c/d"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 7)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(), '"id" = \'a\'\'b\'')
        self.assertEqual(r.rootRule().children()[3].filterExpression(), '"id" = \'a\\nb\'')
        self.assertEqual(r.rootRule().children()[4].filterExpression(), '"id" = \'a\\\\b\'')
        self.assertEqual(r.rootRule().children()[5].filterExpression(), '"id" = \'a\\tb\'')
        self.assertEqual(r.rootRule().children()[6].filterExpression(), '"id" IN (\'c\',\'d\')')

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        cats.append(QgsRendererCategory([3, 4], QgsMarkerSymbol(), "result 3/4"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 3)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), 'id + 1 = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), 'id + 1 = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(), 'id + 1 IN (3,4)')

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        cats.append(QgsRendererCategory([3, 4], QgsMarkerSymbol(), "result 3/4"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 3)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(), '"id" IN (3,4)')

    def testConvertFromGraduatedRenderer(self):
        # Test converting graduated renderer to rule based

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '(id / 2) >= 0.000000 AND (id / 2) <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '(id / 2) > 1.000000 AND (id / 2) <= 2.000000')

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(), '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(), '"id" > 1.000000 AND "id" <= 2.000000')

    def testWillRenderFeatureTwoElse(self):
        """Regression #21287, also test rulesForFeature since there were no tests any where and I've found a couple of issues"""

        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})
        sym4 = QgsFillSymbol.createSimple({'color': '#ff00ff', 'outline_color': 'black'})

        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 200')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 1000, 100000000, 'ELSE')  # <<< - match this!
        self.rx4 = QgsRuleBasedRenderer.Rule(sym4, 1, 999, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)
        rootrule.appendChild(self.rx4)  # <- failed in regression #21287

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Render with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))

        # No context? All rules
        self.assertEqual(len(rootrule.rulesForFeature(ft)), 2)
        self.assertTrue(set(rootrule.rulesForFeature(ft)), set([self.rx3, self.rx4]))

        # With context: only the matching one
        self.assertEqual(len(rootrule.rulesForFeature(ft, ctx)), 1)
        self.assertEqual(rootrule.rulesForFeature(ft, ctx)[0], self.rx3)
        renderer.stopRender(ctx)

    def testUsedAttributes(self):
        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)

        # Create rulebased style
        sym2 = QgsFillSymbol.createSimple({'color': '#71bd6c', 'outline_color': 'black'})
        sym3 = QgsFillSymbol.createSimple({'color': '#1f78b4', 'outline_color': 'black'})

        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 200')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 1000, 100000000, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        renderer = QgsRuleBasedRenderer(rootrule)

        self.assertCountEqual(renderer.usedAttributes(ctx), {'id'})

    def testPointsUsedAttributes(self):
        points_shp = os.path.join(TEST_DATA_DIR, 'points.shp')
        points_layer = QgsVectorLayer(points_shp, 'Points', 'ogr')
        QgsProject.instance().addMapLayer(points_layer)

        # Create rulebased style
        sym1 = QgsMarkerSymbol()
        l1 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l1.setColor(QColor(255, 0, 0))
        l1.setStrokeStyle(Qt.NoPen)
        l1.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle, QgsProperty.fromField("Heading"))
        sym1.changeSymbolLayer(0, l1)

        sym2 = QgsMarkerSymbol()
        l2 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l2.setColor(QColor(0, 255, 0))
        l2.setStrokeStyle(Qt.NoPen)
        l2.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle, QgsProperty.fromField("Heading"))
        sym2.changeSymbolLayer(0, l2)

        sym3 = QgsMarkerSymbol()
        l3 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l3.setColor(QColor(0, 0, 255))
        l3.setStrokeStyle(Qt.NoPen)
        l3.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle, QgsProperty.fromField("Heading"))
        sym3.changeSymbolLayer(0, l3)

        r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"Class" = \'B52\'')
        r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"Class" = \'Biplane\'')
        r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, '"Class" = \'Jet\'')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(r1)
        rootrule.appendChild(r2)
        rootrule.appendChild(r3)

        renderer = QgsRuleBasedRenderer(rootrule)

        points_layer.setRenderer(renderer)

        ms = QgsMapSettings()
        ms.setOutputSize(QSize(400, 400))
        ms.setOutputDpi(96)
        ms.setExtent(QgsRectangle(-133, 22, -70, 52))
        ms.setLayers([points_layer])

        ctx = QgsRenderContext.fromMapSettings(ms)
        ctx.expressionContext().appendScope(points_layer.createExpressionContextScope())

        # for symbol layer
        self.assertCountEqual(l1.usedAttributes(ctx), {'Heading'})
        # for symbol
        self.assertCountEqual(sym1.usedAttributes(ctx), {'Heading'})
        # for symbol renderer
        self.assertCountEqual(renderer.usedAttributes(ctx), {'Class', 'Heading'})

        QgsProject.instance().removeMapLayer(points_layer)

    def testConvertFromEmbedded(self):
        """
        Test converting an embedded symbol renderer to a rule based renderer
        """
        points_layer = QgsVectorLayer('Point', 'Polys', 'memory')
        f = QgsFeature()
        f.setGeometry(QgsGeometry.fromWkt('Point(-100 30)'))
        f.setEmbeddedSymbol(
            QgsMarkerSymbol.createSimple({'name': 'triangle', 'size': 10, 'color': '#ff0000', 'outline_style': 'no'}))
        self.assertTrue(points_layer.dataProvider().addFeature(f))
        f.setGeometry(QgsGeometry.fromWkt('Point(-110 40)'))
        f.setEmbeddedSymbol(
            QgsMarkerSymbol.createSimple({'name': 'square', 'size': 7, 'color': '#00ff00', 'outline_style': 'no'}))
        self.assertTrue(points_layer.dataProvider().addFeature(f))
        f.setGeometry(QgsGeometry.fromWkt('Point(-90 50)'))
        f.setEmbeddedSymbol(None)
        self.assertTrue(points_layer.dataProvider().addFeature(f))

        renderer = QgsEmbeddedSymbolRenderer(defaultSymbol=QgsMarkerSymbol.createSimple({'name': 'star', 'size': 10, 'color': '#ff00ff', 'outline_style': 'no'}))
        points_layer.setRenderer(renderer)

        rule_based = QgsRuleBasedRenderer.convertFromRenderer(renderer, points_layer)
        self.assertEqual(len(rule_based.rootRule().children()), 3)
        rule_0 = rule_based.rootRule().children()[0]
        self.assertEqual(rule_0.filterExpression(), '$id=1')
        self.assertEqual(rule_0.label(), '1')
        self.assertEqual(rule_0.symbol().color().name(), '#ff0000')
        rule_1 = rule_based.rootRule().children()[1]
        self.assertEqual(rule_1.filterExpression(), '$id=2')
        self.assertEqual(rule_1.label(), '2')
        self.assertEqual(rule_1.symbol().color().name(), '#00ff00')
        rule_2 = rule_based.rootRule().children()[2]
        self.assertEqual(rule_2.filterExpression(), 'ELSE')
        self.assertEqual(rule_2.label(), 'All other features')
        self.assertEqual(rule_2.symbol().color().name(), '#ff00ff')

    def testNullsCount(self):

        vl = QgsVectorLayer('Point?crs=epsg:4326&field=number:int',
                            'test', 'memory')

        f = QgsFeature(vl.fields())
        f.setAttribute(0, 0)
        f.setGeometry(QgsGeometry.fromWkt('point(7 45)'))
        vl.dataProvider().addFeatures([f])
        f = QgsFeature(vl.fields())
        f.setGeometry(QgsGeometry.fromWkt('point(7 45)'))
        f.setAttribute(0, 1)
        vl.dataProvider().addFeatures([f])
        f.setGeometry(QgsGeometry.fromWkt('point(7 45)'))
        f = QgsFeature(vl.fields())
        vl.dataProvider().addFeatures([f])

        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), 'one'))
        cats.append(QgsRendererCategory(0, QgsMarkerSymbol(), 'zero'))
        cats.append(QgsRendererCategory(None, QgsMarkerSymbol(), 'NULL'))
        renderer = QgsCategorizedSymbolRenderer("number", cats)

        vl.setRenderer(renderer)

        counter = vl.countSymbolFeatures()
        counter.waitForFinished()

        self.assertEqual(counter.featureCount('0'), 1)
        self.assertEqual(counter.featureCount('1'), 1)
        self.assertEqual(counter.featureCount('2'), 1)
Пример #6
0
class cadastreExport(QObject):

    def __init__(self, layer, etype, comptecommunal, geo_parcelle=None):

        self.plugin_dir = os.path.dirname(__file__)
        self.iface = iface

        # Store an instance of QgsComposition
        self.currentComposition = None

        # Get instance needed for QgsComposition
        self.getMapInstance()

        # type of export : proprietaire or parcelle
        self.etype = etype

        # id of the parcelle
        self.geo_parcelle = geo_parcelle

        # memory layer for redlining
        self.redlineLayer = None

        self.ccFilter = None

        if isinstance(comptecommunal, list):
            self.isMulti = True
            if len(comptecommunal) == 1:
                self.isMulti = False
                comptecommunal = comptecommunal[0].strip(" '")
        else:
            self.isMulti = False

        self.comptecommunal = comptecommunal

        self.maxLineNumber = 15 # max number of line per main table
        self.numPages = 1
        self.pageHeight = 210
        self.pageWidth = 297
        self.printResolution = 300
        self.addExperimentalWatershed = False

        # target directory for saving
        s = QSettings()
        tempDir = s.value("cadastre/tempDir", '%s' % tempfile.gettempdir(), type=str)
        self.targetDir = tempfile.mkdtemp('', 'cad_export_', tempDir)

        # label for header2
        if self.etype == 'proprietaire':
            self.typeLabel = u'DE PROPRIÉTÉ'
        else:
            self.typeLabel = u'PARCELLAIRE'

        self.layer = layer
        self.connectionParams = cadastre_common.getConnectionParameterFromDbLayer(self.layer)
        self.connector = cadastre_common.getConnectorFromUri(self.connectionParams)
        self.dbType = self.connectionParams['dbType']


    def getMapInstance(self):
        '''
        Get instance of object needed to instantiate QgsComposition
        QgsMapRenderer or QgsMapSettings
        Different if context is server
        '''

        # Store an instance of QgsMapRenderer or QgsMapSettings
        # to avoid a nasty bug with 2.4 :
        # https://github.com/3liz/QgisCadastrePlugin/issues/36
        if Qgis.QGIS_VERSION_INT < 20400:
            if self.iface:
                self.mInstance = self.iface.mapCanvas().mapRenderer()
            else:
                self.mInstance = QgsMapRenderer()
        else:
            if self.iface:
                self.mInstance = self.iface.mapCanvas().mapSettings()
            else:
                self.mInstance = QgsMapSettings()

    def setComposerTemplates(self, comptecommunal):
        '''
        Set parameters for given comptecommunal
        '''
        # List of templates
        comptecommunalAbrev = comptecommunal[9:]
        self.composerTemplates = {
            'header1' : {
                'names': ['annee', 'ccodep', 'ccodir', 'ccocom', 'libcom'],
                'position': [3.5, 2.5, 145, 7.5], 'align': [ 128, 4],
                'keepContent' : True,
                'type': 'sql',
                'filter': 'comptecommunal',
                'and': {
                    'proprietaire': u" AND comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND comptecommunal = '%s'" % comptecommunal
                },
                'sticky': True
            },
            'header2' : {
                'names': ['type'],
                'position': [153.5, 2.5, 60, 7.5], 'align': [ 128, 4],
                'keepContent' : True,
                'type': 'properties',
                'source': [self.typeLabel],
                'sticky': True
            },
            'header3' : {
                'names': ['comptecommunal'],
                'position': [218.5, 2.5, 75, 7.5], 'align': [ 128, 2],
                'keepContent' : True,
                'type': 'properties',
                'source': [comptecommunalAbrev],
                'sticky': True
            },
            'proprietaires' : {
                'names': ['lines'],
                'position': [3.5, 10, 290, 40], 'align': [ 32, 1],
                'keepContent' : False,
                'type': 'parent',
                'source': 'proprietaires_line'
            },
            'proprietes_baties' : {
                'names': ['lines'],
                'position': [3.5, 50, 290, 65], 'align': [ 32, 1],
                'keepContent' : False,
                'type': 'parent',
                'source': 'proprietes_baties_line'
            },
            'proprietes_baties_sum' : {
                'names': ['revenucadastral', 'co_vlbaia', 'co_bipevla', 'gp_vlbaia', 'gp_bipevla', 'de_vlbaia', 'de_bipevla', 're_vlbaia', 're_bipevla'],
                'position': [3.5, 115, 290, 15], 'align': [ 32, 1],
                'type': 'sql',
                'keepContent' : True,
                'filter': 'comptecommunal',
                'and': {
                    'proprietaire': u" AND l10.comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND p.parcelle = '%s'" % self.geo_parcelle
                }
            },
            'proprietes_non_baties' : {
                'names': ['lines'],
                'position': [3.5, 130, 290, 65], 'align': [ 32, 1],
                'keepContent' : False,
                'type': 'parent',
                'source': 'proprietes_non_baties_line'
            },
            'proprietes_non_baties_sum' : {
                'names': ['sum_ha_contenance', 'sum_a_contenance', 'sum_ca_contenance', 'sum_drcsuba'],
                'position': [3.5, 195, 290, 13], 'align': [ 32, 1],
                'type': 'sql',
                'keepContent' : True,
                'filter': 'comptecommunal',
                'and': {
                    'proprietaire': u" AND p.comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND p.parcelle = '%s'" % self.geo_parcelle
                },
                'bgcolor': Qt.transparent
            },
            'footer' : {
                'names': ['foot'],
                'position': [3.5, 208, 288, 4], 'align': [ 128, 4],
                'keepContent' : True,
                'type': 'properties',
                'source': [u"Ce document est donné à titre indicatif - Il n'a pas de valeur légale"],
                'bgcolor' : Qt.white,
                'htmlState' : 0,
                'font': QFont('sans-serif', 4, 1, True),
                'sticky': True
            }
        }
        self.mainTables = {
            'proprietaires_line' : {
                'names': ['mainprop', 'epousede', 'adrprop','nele'],
                'type': 'sql',
                'keepContent': True,
                'filter': 'comptecommunal',
                'and': {
                    'proprietaire': u" AND comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND comptecommunal = '%s'" % comptecommunal
                }
            },
            'proprietes_baties_line' : {
                'names': ['section', 'ndeplan', 'ndevoirie', 'adresse', 'coderivoli', 'bat', 'ent', 'niv', 'ndeporte', 'numeroinvar', 'star', 'meval', 'af', 'natloc', 'cat', 'revenucadastral', 'coll', 'natexo', 'anret', 'andeb', 'fractionrcexo', 'pourcentageexo', 'txom', 'coefreduc'],
                'type': 'sql',
                'filter': 'comptecommunal',
                'and': {
                    'proprietaire': u" AND l10.comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND p.parcelle = '%s'" % self.geo_parcelle
                }
            },
            'proprietes_non_baties_line' : {
                'names': ['section', 'ndeplan', 'ndevoirie', 'adresse', 'coderivoli', 'nparcprim', 'fpdp', 'star', 'suf', 'grssgr', 'cl', 'natcult', 'ha_contenance', 'a_contenance', 'ca_contenance', 'revenucadastral', 'coll', 'natexo', 'anret', 'fractionrcexo', 'pourcentageexo', 'tc', 'lff'],
                'type': 'sql',
                'and': {
                    'proprietaire': u" AND p.comptecommunal = '%s'" % comptecommunal,
                    'parcelle': u" AND p.parcelle = '%s'" % self.geo_parcelle
                }
            }

        }

        # items for which to count number of lines
        self.lineCount = {
            'proprietes_baties_line': {'count' : 0, 'data' : None},
            'proprietes_non_baties_line': {'count' : 0, 'data' : None}
        }

        # items for which not the run a query for each page
        # but only once and keep content for next pages
        self.contentKeeped = {}
        for key, item in list(self.composerTemplates.items()):
            if 'keepContent' in item and item['keepContent']:
                self.contentKeeped[key] = ''
        for key, item in list(self.mainTables.items()):
            if 'keepContent' in item and item['keepContent']:
                self.contentKeeped[key] = ''

    def getContentForGivenItem(self, key, item, page=None):
        '''
        Take content from template file
        corresponding to the key
        and assign data from item
        '''
        # First check previous stored content
        if 'keepContent' in item and item['keepContent'] \
         and self.contentKeeped[key]:
            return self.contentKeeped[key]

        content = ''
        replaceDict = ''
        # Build template file path
        tplPath = os.path.join(
            self.plugin_dir,
            "templates/%s.tpl" % key
        )

        # Build replace dict depending on source type
        if item['type'] == 'sql':
            data = None

            # Load SQL query and get data
            # Get sql file
            sqlFile = tplPath + '.sql'
            fin = open(sqlFile, 'rt', encoding='utf-8')
            sql = fin.read()
            fin.close()

            # Add schema to search_path if postgis
            if self.dbType == 'postgis':
                sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
            # Add where clause depending on etype
            sql = sql.replace('$and', item['and'][self.etype] )

            # Limit results if asked
            if page and key in list(self.mainTables.keys()) \
            and key in list(self.lineCount.keys()):
                offset = int((page- 1) * self.maxLineNumber)
                #~ sql+= " LIMIT %s" % self.maxLineNumber
                #~ sql+= " OFFSET %s" % offset
                # Get data from previous fetched full data
                data = self.lineCount[key]['data'][offset:self.maxLineNumber+offset]

            # Run SQL
            if self.dbType == 'spatialite':
                sql = cadastre_common.postgisToSpatialite(sql)
            # Run SQL only if data has not already been defined
            if data is None:
                #print(sql)
                [header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.connector, sql)

            # Page no defined = means the query is here to
            # get line count and whole data for proprietes_baties & proprietes_non_baties
            if not page:
                if key in list(self.lineCount.keys()):
                    # line count
                    self.lineCount[key]['count'] = rowCount
                    # keep data
                    self.lineCount[key]['data'] = data
            if page:
                # Get content for each line of data
                for line in data:
                    replaceDict = {}
                    for i in range(len(item['names'])):
                        replaceDict['$%s' % item['names'][i] ] = u'%s' % line[i]
                    content+= self.getHtmlFromTemplate(tplPath, replaceDict)

                # fill empty data to have full size table
                if key in list(self.mainTables.keys()) \
                and key not in list(self.contentKeeped.keys()) \
                and len(data) < self.maxLineNumber:
                    for l in range(self.maxLineNumber - len(data)):
                        replaceDict = {}
                        for i in range(len(item['names'])):
                            replaceDict['$%s' % item['names'][i] ] = u'&nbsp;'
                        content+= self.getHtmlFromTemplate(tplPath, replaceDict)

        elif item['type'] == 'properties':
            # build replace dict from properties
            replaceDict = {}
            for i in range(len(item['names'])):
                replaceDict['$' + item['names'][i]] = item['source'][i]
            content = self.getHtmlFromTemplate(tplPath, replaceDict)

        elif item['type'] == 'parent':
            replaceDict = {}
            for i in range(len(item['names'])):
                replaceDict['$' + item['names'][i]] = self.mainTables[ item['source'] ]['content']
            content = self.getHtmlFromTemplate(tplPath, replaceDict)

        # Keep somme content globally
        if 'keepContent' in item and item['keepContent']:
            self.contentKeeped[key] = content

        # replace some unwanted content
        content = content.replace('None', '')
        return content


    def getHtmlFromTemplate(self, tplPath, replaceDict):
        '''
        Get the content of a template file
        and replace all variables with given data
        '''
        if self.iface:
            QApplication.setOverrideCursor(Qt.WaitCursor)

        def replfunc(match):
            return replaceDict[match.group(0)]
        regex = re.compile('|'.join(re.escape(x) for x in replaceDict))

        try:
            fin = open(tplPath, 'rt', encoding='utf-8')
            data = fin.read()
            fin.close()
            data = regex.sub(replfunc, data)
            return data

        except IOError as e:
            msg = u"Erreur lors de l'export: %s" % e
            self.go = False
            # fix_print_with_import
            print("%s" % msg)
            return msg

        finally:
            if self.iface:
                QApplication.restoreOverrideCursor()


    def createComposition(self):
        '''
        Create a print Layout
        '''
        c = QgsPrintLayout(QgsProject.instance())
        c.initializeDefaults()
        c.setUnits(QgsUnitTypes.LayoutMillimeters)

        g=QgsLayoutGridSettings(c)
        g.setOffset( QgsLayoutPoint(3.5, 0, QgsUnitTypes.LayoutMillimeters)   )
        g.setResolution( QgsLayoutMeasurement(2.5) )

        # Set page number
        self.getPageNumberNeeded()
        # Set main properties
        for i in range(1, self.numPages):
            p=QgsLayoutItemPage(c)
            #page.setPageSize('A6')
            p.setPageSize( QgsLayoutSize(self.pageWidth, self.pageHeight, QgsUnitTypes.LayoutMillimeters) )
            c.pageCollection().addPage(p)

        # Set the global currentComposition
        self.currentComposition = c

    def getPageNumberNeeded(self):
        '''
        Calculate the minimum pages
        needed to fit all the data
        '''
        # retrieve total data and get total count
        for key in list(self.lineCount.keys()):
            self.getContentForGivenItem(key, self.mainTables[key])
        self.numPages = max(
            [
            1 + int(self.lineCount['proprietes_baties_line']['count'] / self.maxLineNumber),
            1 + int(self.lineCount['proprietes_non_baties_line']['count'] / self.maxLineNumber)
            ]
        )

        # Add a page for map if etype == parcelle
        if self.etype == 'parcelle' and self.iface:
            self.numPages+=1


    def addPageContent(self, page):
        '''
        Add all needed item for a single page
        '''

        # First get content for parent items
        for key, item in list(self.mainTables.items()):
            self.mainTables[key]['content'] = self.getContentForGivenItem(
                key,
                item,
                page
            )

        # Then get content for displayed items
        for key, item in list(self.composerTemplates.items()):
            self.buildComposerLabel(key,item,page)

        # Add watershed
        if self.addExperimentalWatershed:
            w = QgsComposerPicture(self.currentComposition)
            w.setItemPosition(50, (page - 1) * (self.pageHeight + 10), 150, 100)
            w.setFrameEnabled(False)
            pictureFile = os.path.join(
                self.plugin_dir,
                "templates/experimental.svg"
            )
            w.setPictureFile(pictureFile)
            w.setBackgroundEnabled(False)
            w.setTransparency(60)
            self.currentComposition.addItem(w)

    def buildComposerLabel(self, key, item, page):
        '''
        Add a label to the print layout for an item and page
        '''
        cl = QgsLayoutItemLabel(self.currentComposition)

        # 1st page is a map for parcelle
        dpage = page -1
        if self.etype == 'parcelle' and self.iface:
            dpage = page

        cl.attemptMove(
            QgsLayoutPoint(
                item['position'][0],
                item['position'][1]+ (dpage) * (self.pageHeight + 10),
                QgsUnitTypes.LayoutMillimeters
            )
        )
        cl.setFixedSize(
            QgsLayoutSize(
                item['position'][2],
                item['position'][3],
                QgsUnitTypes.LayoutMillimeters
            )
        )

        cl.setVAlign(item['align'][0])
        cl.setHAlign(item['align'][1])
        content = self.getContentForGivenItem(
            key,
            item,
            page
        )

        cl.setMargin(0)
        cl.setMode(1)
        cl.setText(content)
        cl.setFrameEnabled(False)
        if 'bgcolor' in item:
            cl.setBackgroundColor(item['bgcolor'])
        if 'htmlState' in item:
            cl.setMode(item['htmlState'])
        if 'font' in item:
            cl.setFont(item['font'])

        self.currentComposition.addLayoutItem(cl)


    def addParcelleMap(self):
        '''
        Add content in the first page
        with a map and basic information
        '''
        # First add headers
        for key, item in list(self.composerTemplates.items()):
            if 'sticky' in item:
                self.buildComposerLabel(key, item, 0)

        # Get feature extent
        exp = QgsExpression('"geo_parcelle" = \'%s\'' % self.geo_parcelle)
        request = QgsFeatureRequest(exp)
        extent = None
        features = self.layer.getFeatures(request)
        for feature in features:
            geom = feature.geometry()
            peri = geom.length()
            buf = peri / 20
            extent = geom.buffer(buf,5).boundingBox()

        # Add memory layer to highlight parcelle
        if extent:
            if self.redlineLayer:
                QgsProject.instance().removeMapLayer(self.redlineLayer.id())
            crs = self.layer.crs().authid()
            vl = QgsVectorLayer("Polygon?crs=" + crs, "temporary", "memory")
            pr = vl.dataProvider()
            vl.startEditing()
            pr.addFeatures([f for f in self.layer.getFeatures(request)])
            vl.commitChanges()
            vl.updateExtents()
            props = vl.renderer().symbol().symbolLayer(0).properties()
            props['outline_width'] = u'1'
            props['outline_color'] = u'0,85,255,255'
            props['outline_style'] = u'solid'
            props['style'] = u'no'
            vl.renderer().setSymbol(QgsFillSymbol.createSimple(props))
            QgsProject.instance().addMapLayer(vl)
            self.redlineLayer = vl

        # Add composer map & to parcelle
        miLayers = self.mInstance.layers()
        miLayers.insert( 0, vl )
        cm = QgsLayoutItemMap(self.currentComposition)
        cm.updateBoundingRect()
        cm.setRect(QRectF(0, 0, 286, 190))
        cm.setPos(6,15)
        cm.setLayers(QgsProject.instance().mapThemeCollection().masterVisibleLayers())

        if extent:
            cm.zoomToExtent(extent)

        cm.setFrameEnabled(True)
        cm.setBackgroundEnabled(True)
        self.currentComposition.addItem(cm)


    def exportItemAsPdf(self, comptecommunal, suffix=None):
        '''
        Export one PDF file using the template composer
        filled with appropriate data
        for one "compte communal"
        '''
        temppath = None
        # print("export pour le cc %s" % comptecommunal)
        # Set configuration
        self.setComposerTemplates(comptecommunal)

        # Create the composition
        self.createComposition()

        if self.currentComposition:
            if self.iface:
                QApplication.setOverrideCursor(Qt.WaitCursor)
            # Populate composition for all pages
            # print("numpage %s" % self.numPages)
            for i in range(self.numPages):
                j=i+1
                self.addPageContent(j)

            # Add map in first page if export parcelle
            if self.etype == 'parcelle' and self.iface:
                self.addParcelleMap()

            # Create the pdf output path
            from time import time
            temp = "releve_%s_%s_%s.pdf" % (
                self.etype,
                comptecommunal.replace('+', 'plus').replace('*', 'fois'), #.replace('¤', 'plus'),
                int(time()*100)
            )
            # Create regexp to remove all non ascii chars
            import re
            r = re.compile(r"[^ -~]")
            temp = r.sub('', temp)
            #print temp
            temppath = os.path.join(self.targetDir, temp)
            temppath = os.path.normpath(temppath)
            # print("export temppath %s" % temppath)

            # Export as pdf
            exportersettings=QgsLayoutExporter.PdfExportSettings()
            exportersettings.dpi=300
            exportersettings.forceVectorOutput = True
            exportersettings.rasterizeWholeImage = False
            exporter = QgsLayoutExporter(self.currentComposition)
            exporter.exportToPdf(temppath, exportersettings)

            # Remove redline layer
            if self.redlineLayer:
                QgsProject.instance().removeMapLayer(self.redlineLayer.id())

            if self.iface:
                QApplication.restoreOverrideCursor()

            # Opens PDF in default application
            if not self.isMulti and self.iface:
                cadastre_common.openFile(temppath)

        return temppath


    def openFile(self, filename):
        '''
        Opens a file with default system app
        '''
        if sys.platform == "win32":
            os.startfile(filename)
        else:
            opener = "open" if sys.platform == "darwin" else "xdg-open"
            subprocess.call([opener, filename])


    def exportAsPDF(self):
        '''
        Run the PDF export
        '''
        paths = []
        # Export as many pdf as compte communal
        if self.isMulti:
            if self.iface:
                # Show print progress dialog
                self.setupPrintProgressDialog()
            nb = len(self.comptecommunal)
            # Export PDF for each compte
            for comptecommunal in self.comptecommunal:
                # export as PDF
                comptecommunal = comptecommunal.strip("' ")
                apath = self.exportItemAsPdf(comptecommunal)
                if apath:
                    paths.append(apath)

                # update progress bar
                if self.iface:
                    self.printStep+=1
                    self.printProgress.pbPrint.setValue(int(self.printStep * 100 / nb))

            if self.iface:
                info = u"Les relevés ont été enregistrés dans le répertoire :\n%s\n\nOuvrir le dossier ?" % self.targetDir
                openFolder = QDesktopServices()
                openFolder.openUrl(QUrl('file:///%s' % self.targetDir))
        else:
            apath = self.exportItemAsPdf(self.comptecommunal)
            if apath:
                paths.append(apath)
        return paths


    def setupPrintProgressDialog(self):
        '''
        Opens print progress dialog
        '''
        if not self.iface:
            return
        # Show progress dialog
        self.printProgress = cadastrePrintProgress()
        # Set progress bar
        self.printStep = 0
        self.printProgress.pbPrint.setValue(0)
        # Show dialog
        self.printProgress.show()
Пример #7
0
def GDX_Publisher(self):

    #				print ("GDX_Publisher -------------------------------\n")

    tumpdir = unicode(
        QFileInfo(QgsApplication.qgisUserDatabaseFilePath()).path()
    ) + "/python/plugins/gearthview3/_WebServer"

    #				print (tumpdir)

    adesso = str(datetime.datetime.now())
    adesso = adesso.replace(" ", "_")
    adesso = adesso.replace(":", "_")
    adesso = adesso.replace(".", "_")

    #				print ("adesso: <%s>\n" %(adesso))

    # HERE IT DELETES THE OLD IMAGE ------------------------------------
    # (if you comment these, images still remain ...  :)
    for filename in glob.glob(str(tumpdir + '/*.png')):
        os.remove(str(filename))
    for filename in glob.glob(str(tumpdir + '/*.pngw')):
        os.remove(str(filename))
# ------------------------------------------------------------------

    mapCanvas = self.iface.mapCanvas()

    text = mapCanvas.extent().toString()
    text1 = text.replace(",", " ")
    text2 = text1.replace(" : ", ",")

    #				print ("extent: <%s>\n" %(text2))

    layer = mapCanvas.currentLayer()

    #				print ("Layer: <%s>\n" %(layer.name() ))

    extent = mapCanvas.extent()
    crsSrc = mapCanvas.mapSettings().destinationCrs()
    crsDest = QgsCoordinateReferenceSystem(4326)

    try:
        transform = QgsCoordinateTransform(crsSrc, crsDest,
                                           QgsProject.instance())
    except:
        transform = QgsCoordinateTransform(crsSrc, crsDest)

    projectedExtent = transform.transformBoundingBox(extent)

    x1 = projectedExtent.xMinimum()
    y1 = projectedExtent.yMinimum()

    x2 = projectedExtent.xMaximum()
    y2 = projectedExtent.yMinimum()

    x3 = projectedExtent.xMaximum()
    y3 = projectedExtent.yMaximum()

    x4 = projectedExtent.xMinimum()
    y4 = projectedExtent.yMaximum()

    xc = (x1 + x3) / 2.
    yc = (y1 + y3) / 2.

    out_folder = tumpdir

    # create output image and initialize it

    mapRect = mapCanvas.extent()
    width = mapCanvas.width()
    height = mapCanvas.height()
    srs = mapCanvas.mapSettings().destinationCrs()

    #				print (width, height)

    # MINORU
    #				canvas = mapCanvas
    ##				image = QImage(size.width(), size.height(), QImage.Format_ARGB32_Premultiplied)
    #				image = QImage(QSize(math.ceil(width), math.ceil(height)), QImage.Format_ARGB32)
    #				image.fill(Qt.transparent)
    ##				image.fill(QColor(0))
    #				painter = QPainter()
    #				painter.setRenderHint(QPainter.Antialiasing, True)
    #				painter.setRenderHint(QPainter.TextAntialiasing, True)
    #				painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
    ##				painter.setRenderHint(QPainter.transparent, True)
    #
    ##				brush = QtGui.QBrush()
    ##				brush.setColor(QtGui.QColor(0))
    ##				painter.setBackground(self, 0)
    #
    #				painter.begin(image)
    #				canvas.render(painter)
    #				painter.end()
    # MINORU

    # MINORU2

    #				settings = self.exportSettings.mapSettings

    settings = QgsMapSettings()
    #				extent = settings.extent()
    extent = mapCanvas.extent()

    # store old map settings
    #				old_outputSize = settings.outputSize()
    #				old_extent = settings.extent()
    #				old_rotation = settings.rotation()
    #				old_layers = settings.layers()
    #				old_backgroundColor = settings.backgroundColor()

    # map settings
    settings.setOutputSize(QSize(width, height))
    #				settings.setExtent(extent.unrotatedRect())
    #				settings.setRotation(extent.rotation())

    #				if layerids:
    #				   settings.setLayers(tools.getLayersByLayerIds(layerids))

    #				if transp_background:
    settings.setBackgroundColor(QColor(Qt.transparent))

    has_pluginlayer = False
    for layer in settings.layers():
        if layer and layer.type() == QgsMapLayer.PluginLayer:
            has_pluginlayer = True
            break

    # create an image
    image = QImage(width, height, QImage.Format_ARGB32_Premultiplied)
    painter = QPainter()
    painter.begin(image)
    #				if antialias:
    #				   painter.setRenderHint(QPainter.Antialiasing)

    # rendering
    job = QgsMapRendererCustomPainterJob(settings, painter)
    if has_pluginlayer:
        job.renderSynchronously(
        )  # use this method so that TileLayerPlugin layer is rendered correctly
    else:
        job.start()
        job.waitForFinished()
    painter.end()

    # restore map settings
    #				settings.setOutputSize(old_outputSize)
    #				settings.setExtent(old_extent)
    #				settings.setRotation(old_rotation)
    #				settings.setLayers(old_layers)
    #				settings.setBackgroundColor(old_backgroundColor)

    # MINORU2

    kml = codecs.open(out_folder + '/doc.kml', 'w', encoding='utf-8')

    kml.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    kml.write(
        '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">\n'
    )
    kml.write('    <Document>\n')
    kml.write('    	 <name>QGisView</name>\n')
    kml.write('    	 <Snippet maxLines="0"></Snippet>\n')

    #				loc = ("    	 <description><![CDATA[https://map.what3words.com/%.7lf,%.7lf]]></description>\n") %(yc, xc)

    #				kml.write(loc)

    kml.write('	     <open>1</open>\n')

    kml.write('	<Style id="sh_style">\n')
    kml.write('		<PolyStyle>\n')
    kml.write('			<color>7fff8080</color>\n')
    kml.write('		</PolyStyle>\n')
    kml.write('	</Style>\n')
    kml.write('	<StyleMap id="msn_style">\n')
    kml.write('		<Pair>\n')
    kml.write('			<key>normal</key>\n')
    kml.write('			<styleUrl>#sn_style</styleUrl>\n')
    kml.write('		</Pair>\n')
    kml.write('		<Pair>\n')
    kml.write('			<key>highlight</key>\n')
    kml.write('			<styleUrl>#sh_style</styleUrl>\n')
    kml.write('		</Pair>\n')
    kml.write('	</StyleMap>\n')
    kml.write('	<Style id="sn_style">\n')
    kml.write('		<PolyStyle>\n')
    kml.write('			<color>00ff8080</color>\n')
    kml.write('			<fill>0</fill>\n')
    kml.write('		</PolyStyle>\n')
    kml.write('	</Style>\n')

    kml.write('	     <Style id="sh_ylw-pushpin">\n')
    kml.write('	     	<IconStyle>\n')
    kml.write('	     		<scale>1.2</scale>\n')
    kml.write('	     	</IconStyle>\n')
    kml.write('	     	<PolyStyle>\n')
    kml.write('	     		<fill>0</fill>\n')
    kml.write('	     	</PolyStyle>\n')
    kml.write('	     </Style>\n')
    kml.write('	     <Style id="sn_ylw-pushpin">\n')
    kml.write('	     	<PolyStyle>\n')
    kml.write('	     		<fill>0</fill>\n')
    kml.write('	     	</PolyStyle>\n')
    kml.write('	     </Style>\n')
    kml.write('	     <StyleMap id="msn_ylw-pushpin">\n')
    kml.write('	     	<Pair>\n')
    kml.write('	     		<key>normal</key>\n')
    kml.write('	     		<styleUrl>#sn_ylw-pushpin</styleUrl>\n')
    kml.write('	     	</Pair>\n')
    kml.write('	     	<Pair>\n')
    kml.write('	     		<key>highlight</key>\n')
    kml.write('	     		<styleUrl>#sh_ylw-pushpin</styleUrl>\n')
    kml.write('	     	</Pair>\n')
    kml.write('	     </StyleMap>\n')

    kml.write('    <StyleMap id="msn_style">\n')
    kml.write('        <Pair>\n')
    kml.write('            <key>normal</key>\n')
    kml.write('            <styleUrl>#sn_style</styleUrl>\n')
    kml.write('        </Pair>\n')
    kml.write('        <Pair>\n')
    kml.write('            <key>highlight</key>\n')
    kml.write('            <styleUrl>#sh_style</styleUrl>\n')
    kml.write('        </Pair>\n')
    kml.write('    </StyleMap>\n')

    kml.write('	     	<Style id="hl">\n')
    kml.write('	     		<IconStyle>\n')
    kml.write('	     			<scale>0.7</scale>\n')
    kml.write('	     			<Icon>\n')
    kml.write(
        '	     				<href>http://maps.google.com/mapfiles/kml/shapes/placemark_circle_highlight.png</href>\n'
    )
    kml.write('	     			</Icon>\n')
    kml.write('	     		</IconStyle>\n')
    kml.write('	     		<LabelStyle>\n')
    kml.write('	     			<scale>0.7</scale>\n')
    kml.write('	     		</LabelStyle>\n')
    kml.write('	     		<ListStyle>\n')
    kml.write('	     		</ListStyle>\n')
    kml.write('	     	</Style>\n')
    kml.write('	     	<Style id="default">\n')
    kml.write('	     		<IconStyle>\n')
    kml.write('	     			<scale>0.7</scale>\n')
    kml.write('	     			<Icon>\n')
    kml.write(
        '	     				<href>http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png</href>\n'
    )
    kml.write('	     			</Icon>\n')
    kml.write('	     		</IconStyle>\n')
    kml.write('	     		<LabelStyle>\n')
    kml.write('	     			<scale>0.7</scale>\n')
    kml.write('	     		</LabelStyle>\n')
    kml.write('	     		<ListStyle>\n')
    kml.write('	     		</ListStyle>\n')
    kml.write('	     	</Style>\n')
    kml.write('	     	<StyleMap id="default0">\n')
    kml.write('	     		<Pair>\n')
    kml.write('	     			<key>normal</key>\n')
    kml.write('	     			<styleUrl>#default</styleUrl>\n')
    kml.write('	     		</Pair>\n')
    kml.write('	     		<Pair>\n')
    kml.write('	     			<key>highlight</key>\n')
    kml.write('	     			<styleUrl>#hl</styleUrl>\n')
    kml.write('	     		</Pair>\n')
    kml.write('	     	</StyleMap>\n')

    rotazio = 0.0

    rotazio = -(mapCanvas.rotation())

    kml.write('      <Folder>\n')

    xc = (x1 + x3) / 2.
    yc = (y1 + y3) / 2.
    dx = (x3 - x1) * 75000.  #100000.

    kml.write('    		<open>1</open>\n')
    kml.write('    		<NetworkLink>\n')
    kml.write('    		   <name>QGIS_link</name>\n')
    kml.write('    		   <visibility>1</visibility>\n')
    kml.write('    		   <open>1</open>\n')
    kml.write('    		   <Link>\n')
    kml.write('    		      <href>QGIS_link.kmz</href>\n')
    kml.write('    		   </Link>\n')
    kml.write('    		</NetworkLink>\n')

    kml.write('    		<LookAt>\n')
    stringazza = ("    		   <longitude>%lf</longitude>\n") % (xc)
    kml.write(stringazza)
    stringazza = ("    		   <latitude>%lf</latitude>\n") % (yc)
    kml.write(stringazza)
    kml.write('    		   <altitude>0</altitude>\n')

    stringazza = ("    		   <heading>%lf</heading>\n") % (rotazio)
    kml.write(stringazza)

    kml.write('    		   <tilt>0</tilt>\n')
    stringazza = ("    		   <range>%lf</range>\n") % (dx)
    kml.write(stringazza)
    kml.write('    		   <gx:altitudeMode>relativeToGround</gx:altitudeMode>\n')
    kml.write('    		</LookAt>\n')

    kml.write('      <GroundOverlay>\n')
    kml.write('    	 <name>QGisView</name>\n')

    kml.write('    	<Icon>\n')

    xN = projectedExtent.xMinimum()
    yN = projectedExtent.yMinimum()

    nomePNG = ("QGisView_%lf_%lf_%s") % (xN, yN, adesso)
    stringazza = ("    	<href>%s.png</href>\n") % (nomePNG)
    kml.write(stringazza)
    kml.write('    		<viewBoundScale>1.0</viewBoundScale>\n')
    kml.write('    	</Icon>\n')
    kml.write('    	<gx:LatLonQuad>\n')
    kml.write('    		<coordinates>\n')

    stringazza = ("%.7lf,%.7lf,0 %.7lf,%.7lf,0 %.7lf,%.7lf,0 %.7lf,%.7lf,0\n"
                  ) % (x1, y1, x2, y2, x3, y3, x4, y4)
    kml.write(stringazza)

    kml.write('    		</coordinates>\n')
    kml.write('    	</gx:LatLonQuad>\n')
    kml.write('    </GroundOverlay>\n')

    #Export tfw-file
    xScale = (projectedExtent.xMaximum() -
              projectedExtent.xMinimum()) / image.width()
    yScale = (projectedExtent.yMaximum() -
              projectedExtent.yMinimum()) / image.height()

    f = open(out_folder + "/" + nomePNG + ".pngw", 'w')
    f.write(str(xScale) + '\n')
    f.write(str(0) + '\n')
    f.write(str(0) + '\n')
    f.write('-' + str(yScale) + '\n')
    f.write(str(projectedExtent.xMinimum()) + '\n')
    f.write(str(projectedExtent.yMaximum()) + '\n')
    f.write(str(projectedExtent.xMaximum()) + '\n')
    f.write(str(projectedExtent.yMinimum()))
    f.close()

    input_file = out_folder + "/" + nomePNG + ".png"

    #Save the image
    image.save(input_file, "png")

    nomeLay = "gearthview"  # foo default name

    #  Adesso scrivo il vettoriale
    #  Prendo il sistema di riferimento del Layer selezionato ------------------

    curLayer = mapCanvas.currentLayer()

    iface = qgis.utils.iface

    selectedLayers = iface.layerTreeView().selectedLayers()

    if (not selectedLayers):
        #				   print ("selectedLayers is Empty")
        selectedLayers = []
        selectedLayers.append(curLayer)

    for layer in selectedLayers:

        if layer:

            if layer.type() == layer.VectorLayer:

                name = layer.source()
                nomeLayer = layer.name()
                nomeLay = nomeLayer.replace(" ", "_")

                #				    print(layer.name())

                kml.write('    <Folder>\n')
                stringazza = ('			<name>%s</name>\n') % (nomeLay)
                kml.write(stringazza)

                crsSrc = layer.crs()

                crsDest = QgsCoordinateReferenceSystem(4326)  # Wgs84LLH
                xform = QgsCoordinateTransform(crsSrc, crsDest,
                                               QgsProject.instance())

                #----------------------------------------------------------------------------
                #  Trasformo la finestra video in coordinate layer,
                #     per estrarre solo gli elementi visibili
                #----------------------------------------------------------------------------
                #				    mapCanvas = iface.mapCanvas()
                boundBox = mapCanvas.extent()

                xMin = float(boundBox.xMinimum())
                yMin = float(boundBox.yMinimum())

                xMax = float(boundBox.xMaximum())
                yMax = float(boundBox.yMaximum())

                crs2 = mapCanvas.mapSettings().destinationCrs()
                crsSrc2 = QgsCoordinateReferenceSystem(crs2.authid())
                crsDest2 = QgsCoordinateReferenceSystem(layer.crs())
                xform2 = QgsCoordinateTransform(crsSrc2, crsDest2,
                                                QgsProject.instance())

                pt0 = xform2.transform(QgsPointXY(xMin, yMin))
                pt1 = xform2.transform(QgsPointXY(xMax, yMax))

                rect = QgsRectangle(pt0, pt1)

                #				    print ("pt0x: <%s>" %(str(pt0.x())) )
                #				    print ("pt0y: <%s>" %(str(pt0.y())) )
                #				    print ("pt1x: <%s>" %(str(pt1.x())) )
                #				    print ("pt1y: <%s>" %(str(pt1.y())) )

                rq = QgsFeatureRequest(rect)

                iter = layer.getFeatures(rq)

                for feat in iter:

                    nele = feat.id()

                    # fetch geometry
                    geom = feat.geometry()
                    # show some information about the feature

                    #				      print (("GeomType: %d") %(geom.type()))

                    if geom.type() == 0:

                        elem = geom.asPoint()
                        x1 = elem.x()
                        y1 = elem.y()

                        #				        pt1 = xform.transform(QgsPoint(x1, y1))

                        kml.write('	<Placemark>\n')

                        stringazza = ('		<name>%s</name>\n') % (nele)
                        kml.write(stringazza)

                        kml.write('	<styleUrl>#default0</styleUrl>\n')

                        # DESCRIPTION DATA-----------
                        kml.write('	<Snippet maxLines="0"></Snippet>\n')
                        kml.write('	<description><![CDATA[\n')
                        kml.write('<html><body><table border="1">\n')
                        kml.write(
                            '<tr><th>Field Name</th><th>Field Value</th></tr>\n'
                        )

                        # Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = ('<tr><td>%s</td><td>%s</td></tr>\n'
                                          ) % (f.name(), feat[iii])

                            kml.write(stringazza)

                        kml.write('</table></body></html>\n')
                        kml.write(']]></description>\n')

                        # EXTENDED DATA -------------
                        stringazza = (
                            '		<ExtendedData><SchemaData schemaUrl="#%s">\n'
                        ) % (nomeLay)
                        kml.write(stringazza)

                        ## Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = (
                                '				<SimpleData name="%s">%s</SimpleData>\n'
                            ) % (f.name(), feat[iii])

                            if (stringazza.find('<SimpleData name="descrip') ==
                                    -1):
                                kml.write(stringazza)

                        kml.write('		</SchemaData></ExtendedData>\n')
                        # EXTENDED DATA -------------

                        wkt = layer.crs().toWkt()
                        source = osr.SpatialReference()
                        source.ImportFromWkt(wkt)

                        target = osr.SpatialReference()
                        target.ImportFromEPSG(4326)

                        transform = osr.CoordinateTransformation(
                            source, target)

                        testo = geom.asWkt()
                        #				        print (testo)

                        testo = testo.replace("PointZ (", "Point (")
                        testo = testo.replace("PointZM (", "Point (")
                        testo = testo.replace(" 0,", ",")
                        testo = testo.replace(" 0)", ")")

                        geometra = ogr.CreateGeometryFromWkt(testo)
                        geometra.Transform(transform)
                        testoKML = geometra.ExportToKML()
                        kml.write(testoKML)

                        kml.write('	</Placemark>\n')

                    elif geom.type() == 1:

                        elem = geom.asPolyline()

                        kml.write('	<Placemark>\n')

                        stringazza = ('		<name>%s</name>\n') % (nele)
                        kml.write(stringazza)

                        kml.write('	<styleUrl>#default0</styleUrl>\n')

                        # DESCRIPTION DATA-----------
                        kml.write('	<Snippet maxLines="0"></Snippet>\n')
                        kml.write('	<description><![CDATA[\n')
                        kml.write('<html><body><table border="1">\n')
                        kml.write(
                            '<tr><th>Field Name</th><th>Field Value</th></tr>\n'
                        )

                        # Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = ('<tr><td>%s</td><td>%s</td></tr>\n'
                                          ) % (f.name(), feat[iii])

                            kml.write(stringazza)

                        kml.write('</table></body></html>\n')
                        kml.write(']]></description>\n')

                        # EXTENDED DATA -------------
                        stringazza = (
                            '		<ExtendedData><SchemaData schemaUrl="#%s">\n'
                        ) % (nomeLay)
                        kml.write(stringazza)

                        ## Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = (
                                '				<SimpleData name="%s">%s</SimpleData>\n'
                            ) % (f.name(), feat[iii])

                            if (stringazza.find('<SimpleData name="descrip') ==
                                    -1):
                                kml.write(stringazza)

                        kml.write('		</SchemaData></ExtendedData>\n')
                        # EXTENDED DATA -------------

                        wkt = layer.crs().toWkt()
                        source = osr.SpatialReference()
                        source.ImportFromWkt(wkt)

                        target = osr.SpatialReference()
                        target.ImportFromEPSG(4326)

                        transform = osr.CoordinateTransformation(
                            source, target)

                        testo = geom.asWkt()
                        #				        print (testo)

                        testo = testo.replace("LineStringZ (", "LineString (")
                        testo = testo.replace("LineStringZM (", "LineString (")
                        testo = testo.replace(" 0,", ",")
                        testo = testo.replace(" 0)", ")")

                        geometra = ogr.CreateGeometryFromWkt(testo)
                        geometra.Transform(transform)
                        testoKML = geometra.ExportToKML()
                        kml.write(testoKML)

                        kml.write('	</Placemark>\n')

                    elif geom.type() == 2:

                        kml.write('	<Placemark>\n')

                        stringazza = ('		<name>%s</name>\n') % (nele)
                        kml.write(stringazza)
                        kml.write('		<styleUrl>#msn_style</styleUrl>\n')

                        # DESCRIPTION DATA-----------
                        kml.write('	<Snippet maxLines="0"></Snippet>\n')
                        kml.write('	<description><![CDATA[\n')
                        kml.write('<html><body><table border="1">\n')
                        kml.write(
                            '<tr><th>Field Name</th><th>Field Value</th></tr>\n'
                        )

                        # Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = ('<tr><td>%s</td><td>%s</td></tr>\n'
                                          ) % (f.name(), feat[iii])

                            kml.write(stringazza)

                        kml.write('</table></body></html>\n')
                        kml.write(']]></description>\n')

                        # EXTENDED DATA -------------
                        stringazza = (
                            '		<ExtendedData><SchemaData schemaUrl="#%s">\n'
                        ) % (nomeLay)
                        kml.write(stringazza)

                        ## Prendo il contenuto dei campi -------------
                        fff = feat.fields()
                        num = fff.count()
                        iii = -1
                        for f in layer.fields():
                            iii = iii + 1

                            stringazza = (
                                '				<SimpleData name="%s">%s</SimpleData>\n'
                            ) % (f.name(), feat[iii])

                            if (stringazza.find('<SimpleData name="descrip') ==
                                    -1):
                                kml.write(stringazza)

                        kml.write('		</SchemaData></ExtendedData>\n')
                        # EXTENDED DATA -------------

                        testo = geom.asWkt()
                        #				        print (testo)

                        wkt = layer.crs().toWkt()
                        source = osr.SpatialReference()
                        source.ImportFromWkt(wkt)

                        target = osr.SpatialReference()
                        target.ImportFromEPSG(4326)

                        transform = osr.CoordinateTransformation(
                            source, target)

                        testo = testo.replace("PolygonZ (", "Polygon (")
                        testo = testo.replace("PolygonZM (", "Polygon (")
                        testo = testo.replace(" 0,", ",")
                        testo = testo.replace(" 0)", ")")
                        geometra = ogr.CreateGeometryFromWkt(testo)
                        geometra.Transform(transform)
                        testoKML = geometra.ExportToKML()

                        testoKML = testoKML.replace(
                            "<Polygon>",
                            "<Polygon><altitudeMode>relativeToGround</altitudeMode>"
                        )

                        kml.write(testoKML)

                        kml.write('	</Placemark>\n')

                kml.write('  </Folder>\n')

    kml.write('</Folder>\n')

    kml.write('</Document>\n')
    kml.write('</kml>\n')
    kml.close()

    if platform.system() == "Windows":
        os.startfile(out_folder + '/doc.kml')

    if platform.system() == "Darwin":
        os.system("open " + str(out_folder + '/doc.kml'))

    if platform.system() == "Linux":
        os.system("xdg-open " + str(out_folder + '/doc.kml'))
Пример #8
0
class TestQgsRulebasedRenderer(unittest.TestCase):
    def setUp(self):
        myShpFile = os.path.join(TEST_DATA_DIR, 'rectangles.shp')
        layer = QgsVectorLayer(myShpFile, 'Rectangles', 'ogr')
        QgsProject.instance().addMapLayer(layer)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({
            'color': '#fdbf6f',
            'outline_color': 'black'
        })
        sym2 = QgsFillSymbol.createSimple({
            'color': '#71bd6c',
            'outline_color': 'black'
        })
        sym3 = QgsFillSymbol.createSimple({
            'color': '#1f78b4',
            'outline_color': 'black'
        })

        self.r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.r1)
        rootrule.appendChild(self.r2)
        rootrule.appendChild(self.r3)

        layer.setRenderer(QgsRuleBasedRenderer(rootrule))
        self.mapsettings = QgsMapSettings()
        self.mapsettings.setOutputSize(QSize(400, 400))
        self.mapsettings.setOutputDpi(96)
        self.mapsettings.setExtent(QgsRectangle(-163, 22, -70, 52))

        rendered_layers = [layer]
        self.mapsettings.setLayers(rendered_layers)

    def testElse(self):
        # Setup rendering check
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_else')
        result = renderchecker.runTest('rulebased_else')

        assert result

    def testDisabledElse(self):
        # Disable a rule and assert that it's hidden not rendered with else
        self.r2.setActive(False)

        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlName('expected_rulebased_disabled_else')
        result = renderchecker.runTest('rulebased_disabled_else')

        assert result

    def testWillRenderFeature(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        renderer.rootRule().children()[0].setActive(False)
        renderer.rootRule().children()[1].setActive(True)
        renderer.rootRule().children()[2].setActive(True)

        renderer.startRender(ctx, vl.fields())  # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        renderer.rootRule().children()[0].setActive(True)
        assert rendered == False

        renderer.startRender(ctx, vl.fields())  # build mActiveChlidren
        rendered = renderer.willRenderFeature(ft, ctx)
        renderer.stopRender(ctx)
        assert rendered == True

    def testWillRenderFeatureNestedElse(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym1 = QgsFillSymbol.createSimple({
            'color': '#fdbf6f',
            'outline_color': 'black'
        })
        sym2 = QgsFillSymbol.createSimple({
            'color': '#71bd6c',
            'outline_color': 'black'
        })
        sym3 = QgsFillSymbol.createSimple({
            'color': '#1f78b4',
            'outline_color': 'black'
        })

        self.rx1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"id" = 1')
        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 2')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, 'ELSE')

        self.rx3.appendChild(self.rx1)

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Render with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

        # Render with else rule where else is deactivated
        renderer.rootRule().children()[1].setActive(False)
        renderer.startRender(ctx, vl.fields())
        self.assertFalse(renderer.willRenderFeature(ft, ctx))
        renderer.stopRender(ctx)

    def testFeatureCount(self):
        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(2)  # 'id' = 3 => ELSE
        renderer = vl.renderer()

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        counter = vl.countSymbolFeatures()
        counter.waitForFinished()

        renderer.startRender(ctx, vl.fields())

        elseRule = None
        for rule in renderer.rootRule().children():
            if rule.filterExpression() == 'ELSE':
                elseRule = rule

        assert elseRule != None

        cnt = counter.featureCount(elseRule.ruleKey())
        assert cnt == 1

    def testRefineWithCategories(self):
        # Test refining rule with categories (refs #10815)

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r2, c)
        assert self.r2.children()[0].filterExpression() == '"id" = 1'
        assert self.r2.children()[1].filterExpression() == '"id" = 2'

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r1, c)
        assert self.r1.children()[0].filterExpression() == 'id + 1 = 1'
        assert self.r1.children()[1].filterExpression() == 'id + 1 = 2'

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        QgsRuleBasedRenderer.refineRuleCategories(self.r3, c)
        assert self.r3.children()[0].filterExpression() == '"id" = 1'
        assert self.r3.children()[1].filterExpression() == '"id" = 2'

    def testRefineWithRanges(self):
        # Test refining rule with ranges (refs #10815)

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r2, g)
        assert self.r2.children()[0].filterExpression(
        ) == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r2.children()[1].filterExpression(
        ) == '"id" > 1.0000 AND "id" <= 2.0000'

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r1, g)
        assert self.r1.children()[0].filterExpression(
        ) == '(id / 2) >= 0.0000 AND (id / 2) <= 1.0000'
        assert self.r1.children()[1].filterExpression(
        ) == '(id / 2) > 1.0000 AND (id / 2) <= 2.0000'

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        QgsRuleBasedRenderer.refineRuleRanges(self.r3, g)
        assert self.r3.children()[0].filterExpression(
        ) == '"id" >= 0.0000 AND "id" <= 1.0000'
        assert self.r3.children()[1].filterExpression(
        ) == '"id" > 1.0000 AND "id" <= 2.0000'

    def testConvertFromCategorisedRenderer(self):
        # Test converting categorised renderer to rule based

        # First, try with a field based category (id)
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "id 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "id 2"))
        cats.append(QgsRendererCategory('a\'b', QgsMarkerSymbol(), "id a'b"))
        cats.append(QgsRendererCategory('a\nb', QgsMarkerSymbol(), "id a\\nb"))
        cats.append(QgsRendererCategory('a\\b', QgsMarkerSymbol(),
                                        "id a\\\\b"))
        cats.append(QgsRendererCategory('a\tb', QgsMarkerSymbol(), "id a\\tb"))
        cats.append(QgsRendererCategory(['c', 'd'], QgsMarkerSymbol(), "c/d"))
        c = QgsCategorizedSymbolRenderer("id", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 7)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(),
                         '"id" = \'a\'\'b\'')
        self.assertEqual(r.rootRule().children()[3].filterExpression(),
                         '"id" = \'a\\nb\'')
        self.assertEqual(r.rootRule().children()[4].filterExpression(),
                         '"id" = \'a\\\\b\'')
        self.assertEqual(r.rootRule().children()[5].filterExpression(),
                         '"id" = \'a\\tb\'')
        self.assertEqual(r.rootRule().children()[6].filterExpression(),
                         '"id" IN (\'c\',\'d\')')

        # Next try with an expression based category
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        cats.append(
            QgsRendererCategory([3, 4], QgsMarkerSymbol(), "result 3/4"))
        c = QgsCategorizedSymbolRenderer("id + 1", cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 3)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         'id + 1 = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         'id + 1 = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(),
                         'id + 1 IN (3,4)')

        # Last try with an expression which is just a quoted field name
        cats = []
        cats.append(QgsRendererCategory(1, QgsMarkerSymbol(), "result 1"))
        cats.append(QgsRendererCategory(2, QgsMarkerSymbol(), "result 2"))
        cats.append(
            QgsRendererCategory([3, 4], QgsMarkerSymbol(), "result 3/4"))
        c = QgsCategorizedSymbolRenderer('"id"', cats)

        r = QgsRuleBasedRenderer.convertFromRenderer(c)
        self.assertEqual(len(r.rootRule().children()), 3)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         '"id" = 1')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         '"id" = 2')
        self.assertEqual(r.rootRule().children()[2].filterExpression(),
                         '"id" IN (3,4)')

    def testConvertFromGraduatedRenderer(self):
        # Test converting graduated renderer to rule based

        # First, try with a field based category (id)
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         '"id" > 1.000000 AND "id" <= 2.000000')

        # Next try with an expression based range
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer("id / 2", ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         '(id / 2) >= 0.000000 AND (id / 2) <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         '(id / 2) > 1.000000 AND (id / 2) <= 2.000000')

        # Last try with an expression which is just a quoted field name
        ranges = []
        ranges.append(QgsRendererRange(0, 1, QgsMarkerSymbol(), "0-1"))
        ranges.append(QgsRendererRange(1, 2, QgsMarkerSymbol(), "1-2"))
        g = QgsGraduatedSymbolRenderer('"id"', ranges)

        r = QgsRuleBasedRenderer.convertFromRenderer(g)
        self.assertEqual(r.rootRule().children()[0].filterExpression(),
                         '"id" >= 0.000000 AND "id" <= 1.000000')
        self.assertEqual(r.rootRule().children()[1].filterExpression(),
                         '"id" > 1.000000 AND "id" <= 2.000000')

    def testWillRenderFeatureTwoElse(self):
        """Regression #21287, also test rulesForFeature since there were no tests any where and I've found a couple of issues"""

        vl = self.mapsettings.layers()[0]
        ft = vl.getFeature(0)  # 'id' = 1

        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
        ctx.expressionContext().setFeature(ft)

        # Create rulebased style
        sym2 = QgsFillSymbol.createSimple({
            'color': '#71bd6c',
            'outline_color': 'black'
        })
        sym3 = QgsFillSymbol.createSimple({
            'color': '#1f78b4',
            'outline_color': 'black'
        })
        sym4 = QgsFillSymbol.createSimple({
            'color': '#ff00ff',
            'outline_color': 'black'
        })

        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 200')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 1000, 100000000,
                                             'ELSE')  # <<< - match this!
        self.rx4 = QgsRuleBasedRenderer.Rule(sym4, 0.1, 999, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)
        rootrule.appendChild(self.rx4)  # <- failed in regression #21287

        vl.setRenderer(QgsRuleBasedRenderer(rootrule))
        renderer = vl.renderer()

        # Render with else rule and all activated
        renderer.startRender(ctx, vl.fields())
        self.assertTrue(renderer.willRenderFeature(ft, ctx))

        # No context? All rules
        self.assertEqual(len(rootrule.rulesForFeature(ft)), 2)
        self.assertTrue(set(rootrule.rulesForFeature(ft)),
                        set([self.rx3, self.rx4]))

        # With context: only the matching one
        self.assertEqual(len(rootrule.rulesForFeature(ft, ctx)), 1)
        self.assertEqual(rootrule.rulesForFeature(ft, ctx)[0], self.rx3)
        renderer.stopRender(ctx)

    def testUsedAttributes(self):
        ctx = QgsRenderContext.fromMapSettings(self.mapsettings)

        # Create rulebased style
        sym2 = QgsFillSymbol.createSimple({
            'color': '#71bd6c',
            'outline_color': 'black'
        })
        sym3 = QgsFillSymbol.createSimple({
            'color': '#1f78b4',
            'outline_color': 'black'
        })

        self.rx2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"id" = 200')
        self.rx3 = QgsRuleBasedRenderer.Rule(sym3, 1000, 100000000, 'ELSE')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(self.rx2)
        rootrule.appendChild(self.rx3)

        renderer = QgsRuleBasedRenderer(rootrule)

        self.assertCountEqual(renderer.usedAttributes(ctx), {'id'})

    def testPointsUsedAttributes(self):
        points_shp = os.path.join(TEST_DATA_DIR, 'points.shp')
        points_layer = QgsVectorLayer(points_shp, 'Points', 'ogr')
        QgsProject.instance().addMapLayer(points_layer)

        # Create rulebased style
        sym1 = QgsMarkerSymbol()
        l1 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l1.setColor(QColor(255, 0, 0))
        l1.setStrokeStyle(Qt.NoPen)
        l1.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle,
                                  QgsProperty.fromField("Heading"))
        sym1.changeSymbolLayer(0, l1)

        sym2 = QgsMarkerSymbol()
        l2 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l2.setColor(QColor(0, 255, 0))
        l2.setStrokeStyle(Qt.NoPen)
        l2.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle,
                                  QgsProperty.fromField("Heading"))
        sym2.changeSymbolLayer(0, l2)

        sym3 = QgsMarkerSymbol()
        l3 = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 5)
        l3.setColor(QColor(0, 0, 255))
        l3.setStrokeStyle(Qt.NoPen)
        l3.setDataDefinedProperty(QgsSymbolLayer.PropertyAngle,
                                  QgsProperty.fromField("Heading"))
        sym3.changeSymbolLayer(0, l3)

        r1 = QgsRuleBasedRenderer.Rule(sym1, 0, 0, '"Class" = \'B52\'')
        r2 = QgsRuleBasedRenderer.Rule(sym2, 0, 0, '"Class" = \'Biplane\'')
        r3 = QgsRuleBasedRenderer.Rule(sym3, 0, 0, '"Class" = \'Jet\'')

        rootrule = QgsRuleBasedRenderer.Rule(None)
        rootrule.appendChild(r1)
        rootrule.appendChild(r2)
        rootrule.appendChild(r3)

        renderer = QgsRuleBasedRenderer(rootrule)

        points_layer.setRenderer(renderer)

        ms = QgsMapSettings()
        ms.setOutputSize(QSize(400, 400))
        ms.setOutputDpi(96)
        ms.setExtent(QgsRectangle(-133, 22, -70, 52))
        ms.setLayers([points_layer])

        ctx = QgsRenderContext.fromMapSettings(ms)
        ctx.expressionContext().appendScope(
            points_layer.createExpressionContextScope())

        # for symbol layer
        self.assertCountEqual(l1.usedAttributes(ctx), {'Heading'})
        # for symbol
        self.assertCountEqual(sym1.usedAttributes(ctx), {'Heading'})
        # for symbol renderer
        self.assertCountEqual(renderer.usedAttributes(ctx),
                              {'Class', 'Heading'})

        QgsProject.instance().removeMapLayer(points_layer)