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())
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())
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)
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' ' 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()
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'))
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)