def test_selected_feature_values(self): """ Test value collection for selected features """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) # default plot settings settings = PlotSettings('scatter') settings.properties['selected_features_only'] = True settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' factory = PlotFactory(settings) # no selection, no values self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, []) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, []) vl1.selectByIds([1, 3, 4]) factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [88, 329, 319]) self.assertEqual(factory.settings.y, [22.26, 35.05, 46.64]) vl1.selectByIds([]) factory = PlotFactory(settings) self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, [])
def test_data_defined_stroke_color(self): """ Test data defined stroke color """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'mg' settings.properties['in_color'] = 'red' factory = PlotFactory(settings) # should be empty, not using data defined size self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.data_defined_colors, []) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable('some_var', 10) context.appendScope(scope) context.appendScope(vl1.createExpressionContextScope()) return context generator = TestGenerator() settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_STROKE_COLOR, QgsProperty.fromExpression( 'case when round("ca"/@some_var)>10 then \'yellow\' else \'blue\' end' )) factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.data_defined_stroke_colors, [ '#0000ff', '#0000ff', '#0000ff', '#0000ff', '#0000ff', '#ffff00', '#ffff00', '#ffff00', '#ffff00' ])
def test_data_defined_histogram_color(self): """ Test data defined stroke color """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.geojson') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('histogram') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [ 203, 151, 350, 137, 319, 329, 267, 88, 98, 84, 100, 627, 306, 513, 267, 457, 683, 791, 788, 265, 296, 680, 536, 1122, 632, 1055, 1322 ]) self.assertEqual(factory.settings.data_defined_colors, []) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() context.appendScope(scope) context.appendScope(vl1.createExpressionContextScope()) return context generator = TestGenerator() settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_COLOR, QgsProperty.fromExpression("""array('215,25,28,255', '241,124,74,255', '254,201,128,255', '255,255,191,255', '199,230,219,255', '129,186,216,255', '44,123,182,255')""")) factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [ 203, 151, 350, 137, 319, 329, 267, 88, 98, 84, 100, 627, 306, 513, 267, 457, 683, 791, 788, 265, 296, 680, 536, 1122, 632, 1055, 1322 ]) self.assertEqual(factory.settings.y, []) self.assertEqual(factory.settings.data_defined_colors, [ "#d7191c", "#f17c4a", "#fec980", "#ffffbf", "#c7e6db", "#81bad8", "#2c7bb6" ])
def test_data_defined_sizes(self): """ Test data defined marker sizes """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'mg' settings.properties['marker_size'] = 15 factory = PlotFactory(settings) # should be empty, not using data defined size self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.data_defined_marker_sizes, []) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable('some_var', 10) context.appendScope(scope) context.appendScope(vl1.createExpressionContextScope()) return context generator = TestGenerator() settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_MARKER_SIZE, QgsProperty.fromExpression('round("ca"/@some_var *@value)')) factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual( factory.settings.data_defined_marker_sizes, [123.0, 33.0, 111.0, 53.0, 70.0, 190.0, 175.0, 162.0, 166.0])
def test_selected_feature_values_dynamic(self): """ Test that factory proactively updates when a selection changes, when desired """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) # not using selected features settings = PlotSettings('scatter') settings.properties['selected_features_only'] = False settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' factory = PlotFactory(settings) spy = QSignalSpy(factory.plot_built) vl1.selectByIds([1, 3, 4]) self.assertEqual(len(spy), 0) # using selected features settings = PlotSettings('scatter') settings.properties['selected_features_only'] = True settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' factory = PlotFactory(settings) spy = QSignalSpy(factory.plot_built) vl1.selectByIds([1]) self.assertEqual(len(spy), 1) self.assertEqual(factory.settings.x, [88]) self.assertEqual(factory.settings.y, [22.26]) vl1.selectByIds([1, 3, 4]) self.assertEqual(len(spy), 2) self.assertEqual(factory.settings.x, [88, 329, 319]) self.assertEqual(factory.settings.y, [22.26, 35.05, 46.64]) vl1.selectByIds([]) self.assertEqual(len(spy), 3) self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, [])
def create_plot(self): polygon_filter, visible_features_only = self.get_polygon_filter(0) config = {'displayModeBar': False, 'staticPlot': True} if len(self.plot_settings) == 1: plot_factory = PlotFactory(self.plot_settings[0], self, polygon_filter=polygon_filter) self.plot_settings[0].properties['visible_features_only'] = visible_features_only return plot_factory.build_html(config) # to plot many plots in the same figure elif len(self.plot_settings) > 1: # plot list ready to be called within go.Figure pl = [] plot_factory = PlotFactory(self.plot_settings[0], self, polygon_filter=polygon_filter) for current, plot_setting in enumerate(self.plot_settings): polygon_filter, visible_features_only = self.get_polygon_filter(current) plot_setting.properties['visible_features_only'] = visible_features_only factory = PlotFactory(plot_setting, self, polygon_filter=polygon_filter) pl.append(factory.trace[0]) plot_path = plot_factory.build_figures(self.plot_settings[0].plot_type, pl, config=config) with open(plot_path, 'r') as myfile: return myfile.read()
def create_plot(self): if self.linked_map and self.filter_by_map: polygon_filter = FilterRegion(QgsGeometry.fromQPolygonF(self.linked_map.visibleExtentPolygon()), self.linked_map.crs()) visible_features_only = True elif self.filter_by_atlas and self.layout().reportContext().layer() and self.layout().reportContext().feature().isValid(): polygon_filter = FilterRegion(self.layout().reportContext().currentGeometry(), self.layout().reportContext().layer().crs()) visible_features_only = True else: polygon_filter = None visible_features_only = False config = {'displayModeBar': False, 'staticPlot': True} if len(self.plot_settings) == 1: plot_factory = PlotFactory(self.plot_settings[0], self, polygon_filter=polygon_filter) self.plot_settings[0].properties['visible_features_only'] = visible_features_only return plot_factory.build_html(config) # to plot many plots in the same figure elif len(self.plot_settings) > 1: # plot list ready to be called within go.Figure pl = [] plot_factory = PlotFactory(self.plot_settings[0], self, polygon_filter=polygon_filter) for plot_setting in self.plot_settings: plot_setting.properties['visible_features_only'] = visible_features_only factory = PlotFactory(plot_setting, self, polygon_filter=polygon_filter) pl.append(factory.trace[0]) plot_path = plot_factory.build_figures(self.plot_settings[0].plot_type, pl, config=config) with open(plot_path, 'r') as myfile: return myfile.read()
def test_visible_features(self): """ Test filtering to visible features only """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) # not using visible features settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' rect = QgsReferencedRectangle(QgsRectangle(10.1, 43.5, 10.8, 43.85), QgsCoordinateReferenceSystem(4326)) factory = PlotFactory(settings, visible_region=rect) spy = QSignalSpy(factory.plot_built) self.assertEqual(len(spy), 0) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual(factory.settings.y, [ 81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45 ]) settings.properties['visible_features_only'] = True factory = PlotFactory(settings, visible_region=rect) spy = QSignalSpy(factory.plot_built) self.assertEqual(factory.settings.x, [88, 350, 151, 203]) self.assertEqual(factory.settings.y, [22.26, 116.44, 108.25, 110.45]) factory.set_visible_region( QgsReferencedRectangle(QgsRectangle(10.6, 43.1, 12, 43.8), QgsCoordinateReferenceSystem(4326))) self.assertEqual(len(spy), 1) self.assertEqual(factory.settings.x, [98, 267, 319, 137]) self.assertEqual(factory.settings.y, [81.87, 74.16, 46.64, 126.73]) # with reprojection factory.set_visible_region( QgsReferencedRectangle( QgsRectangle(1167379, 5310986, 1367180, 5391728), QgsCoordinateReferenceSystem(3857))) self.assertEqual(len(spy), 2) self.assertEqual(factory.settings.x, [98, 267, 329, 319, 137]) self.assertEqual(factory.settings.y, [81.87, 74.16, 35.05, 46.64, 126.73])
def test_filter(self): """ Test that filters are correctly applied """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'mg' settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_FILTER, QgsProperty.fromExpression('so4/@some_var > 20')) factory = PlotFactory(settings) # should be empty, variable is not available self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, []) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, []) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable('some_var', 10) context.appendScope(scope) return context generator = TestGenerator() factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [267, 329, 319, 350, 203]) self.assertEqual(factory.settings.y, [85.26, 81.11, 131.59, 112.88, 78.34]) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, [])
def test_expression_context(self): """ Test that correct expression context is used when evaluating expressions """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = '"so4"/@some_var' settings.properties['y_name'] = 'mg' factory = PlotFactory(settings) # should be empty, variable is not available self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, []) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, []) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable('some_var', 10) context.appendScope(scope) return context generator = TestGenerator() factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [9.8, 8.8, 26.7, 32.9, 31.9, 13.7, 35.0, 15.1, 20.3]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, [])
def create_plot(self): if self.linked_map and self.filter_by_map: polygon_filter = FilterRegion( QgsGeometry.fromQPolygonF( self.linked_map.visibleExtentPolygon()), self.linked_map.crs()) self.plot_settings.properties['visible_features_only'] = True elif self.filter_by_atlas and self.layout().reportContext().layer( ) and self.layout().reportContext().feature().isValid(): polygon_filter = FilterRegion( self.layout().reportContext().currentGeometry(), self.layout().reportContext().layer().crs()) self.plot_settings.properties['visible_features_only'] = True else: polygon_filter = None self.plot_settings.properties['visible_features_only'] = False factory = PlotFactory(self.plot_settings, self, polygon_filter=polygon_filter) config = {'displayModeBar': False, 'staticPlot': True} return factory.build_html(config)
def test_dates(self): # pylint: disable=too-many-statements """ Test handling of dates """ # default plot settings settings = PlotSettings('scatter') # no source layer, fixed values must be used settings.source_layer_id = '' settings.x = [QDate(2020, 1, 1), QDate(2020, 2, 1), QDate(2020, 3, 1)] settings.y = [4, 5, 6] factory = PlotFactory(settings) # Build the dictionary from teh figure plot_dict = factory.build_plot_dict() # get the x and y fields as list for items in plot_dict['data']: # converts the QDate into strings x = [str(i.toPyDate()) for i in items['x']] y = items['y'] self.assertEqual(x, ["2020-01-01", "2020-02-01", "2020-03-01"]) self.assertEqual(y, [4, 5, 6]) settings.x = [ QDateTime(2020, 1, 1, 11, 21), QDateTime(2020, 2, 1, 0, 15), QDateTime(2020, 3, 1, 17, 23, 11) ] settings.y = [4, 5, 6] factory = PlotFactory(settings) # Build the dictionary from teh figure plot_dict = factory.build_plot_dict() # get the x and y fields as list for items in plot_dict['data']: # converts the QDate into strings x = [str(i.toString(Qt.ISODate)) for i in items['x']] y = items['y'] self.assertEqual(x, [ "2020-01-01T11:21:00", "2020-02-01T00:15:00", "2020-03-01T17:23:11" ]) self.assertEqual(y, [4, 5, 6])
def test_changed_feature_values_dynamic(self): """ Test that factory proactively updates when a layer changes """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) # not using selected features settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' factory = PlotFactory(settings) spy = QSignalSpy(factory.plot_built) self.assertEqual(len(spy), 0) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual(factory.settings.y, [ 81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45 ]) self.assertTrue(vl1.startEditing()) vl1.changeAttributeValue(1, vl1.fields().lookupField('so4'), 500) self.assertEqual(len(spy), 1) self.assertEqual(factory.settings.x, [98, 500, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual(factory.settings.y, [ 81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45 ]) vl1.rollBack()
def test_dates(self): # pylint: disable=too-many-statements """ Test handling of dates """ # default plot settings settings = PlotSettings('scatter') # no source layer, fixed values must be used settings.source_layer_id = '' settings.x = [QDate(2020, 1, 1), QDate(2020, 2, 1), QDate(2020, 3, 1)] settings.y = [4, 5, 6] factory = PlotFactory(settings) # Build the HTML/JavaScript for the plot plot_html = factory.build_html({}) # Find the plot specification in the HTML match = re.search(r'\[.*\]', plot_html) plot_dictionary = json.loads(match.group(0))[0] self.assertEqual(plot_dictionary['x'], ["2020-01-01", "2020-02-01", "2020-03-01"]) self.assertEqual(plot_dictionary['y'], [4, 5, 6]) settings.x = [ QDateTime(2020, 1, 1, 11, 21), QDateTime(2020, 2, 1, 0, 15), QDateTime(2020, 3, 1, 17, 23, 11) ] settings.y = [4, 5, 6] factory = PlotFactory(settings) # Build the HTML/JavaScript for the plot plot_html = factory.build_html({}) # Find the plot specification in the HTML match = re.search(r'\[.*\]', plot_html) plot_dictionary = json.loads(match.group(0))[0] self.assertEqual(plot_dictionary['x'], [ "2020-01-01T11:21:00", "2020-02-01T00:15:00", "2020-03-01T17:23:11" ]) self.assertEqual(plot_dictionary['y'], [4, 5, 6])
def create_plot(self): factory = PlotFactory(self.plot_settings) config = {'displayModeBar': False, 'staticPlot': True} return factory.build_html(config)
def test_values(self): # pylint: disable=too-many-statements """ Test value collection """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) # default plot settings settings = PlotSettings('scatter') # no source layer, fixed values must be used settings.source_layer_id = '' settings.x = [1, 2, 3] settings.y = [4, 5, 6] settings.z = [7, 8, 9] factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [1, 2, 3]) self.assertEqual(factory.settings.y, [4, 5, 6]) self.assertEqual(factory.settings.z, [7, 8, 9]) self.assertEqual(factory.settings.additional_hover_text, []) # use source layer settings.source_layer_id = vl1.id() # no source set => no values factory = PlotFactory(settings) self.assertEqual(factory.settings.x, []) self.assertEqual(factory.settings.y, []) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, []) settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'ca' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual(factory.settings.y, [ 81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45 ]) self.assertEqual(factory.settings.z, []) self.assertEqual(factory.settings.additional_hover_text, []) # with z settings.properties['z_name'] = 'mg' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual(factory.settings.y, [ 81.87, 22.26, 74.16, 35.05, 46.64, 126.73, 116.44, 108.25, 110.45 ]) self.assertEqual( factory.settings.z, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.additional_hover_text, []) # with expressions settings.properties['x_name'] = '"so4"/10' settings.properties[ 'y_name'] = 'case when "profm" >-16 then "ca" else "mg" end' settings.properties['z_name'] = 'case when $x < 10.5 then 1 else 0 end' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [9.8, 8.8, 26.7, 32.9, 31.9, 13.7, 35.0, 15.1, 20.3]) self.assertEqual( factory.settings.y, [81.87, 86.03, 85.26, 35.05, 131.59, 95.36, 112.88, 108.25, 78.34]) self.assertEqual(factory.settings.z, [0, 1, 0, 0, 0, 0, 0, 1, 1]) self.assertEqual(factory.settings.additional_hover_text, []) # with some nulls settings.properties['x_name'] = '"so4"/10' settings.properties[ 'y_name'] = 'case when "profm" >-16 then "ca" else "mg" end' settings.properties[ 'z_name'] = 'case when $x < 10.5 then NULL else 1 end' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [9.8, 26.7, 32.9, 31.9, 13.7, 35.0]) self.assertEqual(factory.settings.y, [81.87, 85.26, 35.05, 131.59, 95.36, 112.88]) self.assertEqual(factory.settings.z, [1, 1, 1, 1, 1, 1]) self.assertEqual(factory.settings.additional_hover_text, []) # with additional values settings.layout['additional_info_expression'] = 'id' factory = PlotFactory(settings) self.assertEqual(factory.settings.x, [9.8, 26.7, 32.9, 31.9, 13.7, 35.0]) self.assertEqual(factory.settings.y, [81.87, 85.26, 35.05, 131.59, 95.36, 112.88]) self.assertEqual(factory.settings.z, [1, 1, 1, 1, 1, 1]) self.assertEqual(factory.settings.additional_hover_text, [9, 7, 6, 5, 4, 3])
def processAlgorithm(self, parameters, context, feedback): """ :param parameters: :param context: """ layer = self.parameterAsSource(parameters, self.INPUT, context) fields = layer.fields() xfieldname = self.parameterAsString(parameters, self.XFIELD, context) yfieldname = self.parameterAsString(parameters, self.YFIELD, context) outputHtmlFile = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) outputJsonFile = self.parameterAsFileOutput(parameters, self.OUTPUT_JSON_FILE, context) plot_type = 'bar' plot_type_input = self.parameterAsInt(parameters, self.PLOT_TYPE, context) plot_type = self.PLOT_TYPE_OPTIONS[plot_type_input] plot_title = self.parameterAsString(parameters, self.PLOT_TITLE, context) in_color_input = self.parameterAsInt(parameters, self.IN_COLOR, context) in_color_hex = self.IN_COLOR_OPTIONS[in_color_input] in_color_html = self.parameterAsString(parameters, self.IN_COLOR_HTML, context) # Some controls msg = [] if plot_type in self.X_MANDATORY and not xfieldname: msg.append(self.tr("The chosen plot type needs a X field !")) if plot_type in self.Y_MANDATORY and not yfieldname: msg.append(self.tr("The chosen plot type needs a Y field !")) if msg: feedback.reportError(' '.join(msg)) raise QgsProcessingException(msg) # Build needed dictionary settings = PlotSettings(plot_type) properties = {} # Add X dimension x_title = '' if xfieldname: # get field index for x idxX = layer.fields().lookupField(xfieldname) # get list of values for x x_var = [ i[xfieldname] for i in layer.getFeatures(QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([idxX ])) ] fieldTypeX = fields[idxX].type() x_title = fields[idxX].alias() or xfieldname properties.x = x_var # Add Y dimension y_title = '' if yfieldname: # get field index for y idxY = layer.fields().lookupField(yfieldname) # get list of values for y y_var = [ i[yfieldname] for i in layer.getFeatures(QgsFeatureRequest().setFlags( QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([idxY ])) ] y_title = fields[idxY].alias() or yfieldname properties.y = y_var # Draw only markers for scatter plot if plot_type in ['scatter', 'polar']: properties['marker'] = 'markers' # Colours properties['in_color'] = in_color_html or in_color_hex or 'DodgerBlue' # Add layout layout = {'title': plot_title or layer.sourceName()} if plot_type in self.X_MANDATORY: layout['x_title'] = x_title if plot_type in self.Y_MANDATORY: layout['y_title'] = y_title settings = PlotSettings(plot_type, properties=properties, layout=layout) # Create plot instance factory = PlotFactory(settings) # Prepare results results = {self.OUTPUT_HTML_FILE: None, self.OUTPUT_JSON_FILE: None} # Save plot as HTML if outputHtmlFile: standalone_plot_path = factory.build_figure() if os.path.isfile(standalone_plot_path): # html file output copyfile(standalone_plot_path, outputHtmlFile) results[self.OUTPUT_HTML_FILE] = outputHtmlFile # Save plot as JSON if outputJsonFile: ojson = {'data': trace, 'layout': plot_layout} with codecs.open(outputJsonFile, 'w', encoding='utf-8') as f: f.write(json.dumps(ojson)) results[self.OUTPUT_JSON_FILE] = outputJsonFile return results
def test_data_defined_layout_properties(self): # pylint: disable=too-many-statements """ Test data defined stroke color """ layer_path = os.path.join(os.path.dirname(__file__), 'test_layer.shp') vl1 = QgsVectorLayer(layer_path, 'test_layer', 'ogr') vl1.setSubsetString('id < 10') self.assertTrue(vl1.isValid()) QgsProject.instance().addMapLayer(vl1) settings = PlotSettings('scatter') settings.source_layer_id = vl1.id() settings.properties['x_name'] = 'so4' settings.properties['y_name'] = 'mg' settings.layout['title'] = 'title' settings.layout['legend_title'] = 'legend_title' settings.layout['x_title'] = 'x_title' settings.layout['y_title'] = 'y_title' settings.layout['z_title'] = 'z_title' settings.layout['x_min'] = 0 settings.layout['x_max'] = 1 settings.layout['y_min'] = 0 settings.layout['y_max'] = 1 factory = PlotFactory(settings) # should be empty, not using data defined size self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.data_defined_title, '') self.assertEqual(factory.settings.data_defined_legend_title, '') self.assertEqual(factory.settings.data_defined_x_title, '') self.assertEqual(factory.settings.data_defined_y_title, '') self.assertEqual(factory.settings.data_defined_z_title, '') self.assertEqual(factory.settings.data_defined_x_min, None) self.assertEqual(factory.settings.data_defined_x_max, None) self.assertEqual(factory.settings.data_defined_y_min, None) self.assertEqual(factory.settings.data_defined_y_max, None) class TestGenerator(QgsExpressionContextGenerator): # pylint: disable=missing-docstring, too-few-public-methods def createExpressionContext(self) -> QgsExpressionContext: # pylint: disable=missing-docstring, no-self-use context = QgsExpressionContext() scope = QgsExpressionContextScope() scope.setVariable('some_var', 10) context.appendScope(scope) context.appendScope(vl1.createExpressionContextScope()) return context generator = TestGenerator() settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_TITLE, QgsProperty.fromExpression("concat('my', '_title_', @some_var)")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_LEGEND_TITLE, QgsProperty.fromExpression("concat('my', '_legend_', @some_var)")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_X_TITLE, QgsProperty.fromExpression("concat('my', '_x_axis_', @some_var)")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_Y_TITLE, QgsProperty.fromExpression("concat('my', '_y_axis_', @some_var)")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_Z_TITLE, QgsProperty.fromExpression("concat('my', '_z_axis_', @some_var)")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_X_MIN, QgsProperty.fromExpression("-1*@some_var")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_X_MAX, QgsProperty.fromExpression("+1*@some_var")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_Y_MIN, QgsProperty.fromExpression("-1*@some_var")) settings.data_defined_properties.setProperty( PlotSettings.PROPERTY_Y_MAX, QgsProperty.fromExpression("+1*@some_var")) factory = PlotFactory(settings, context_generator=generator) self.assertEqual(factory.settings.x, [98, 88, 267, 329, 319, 137, 350, 151, 203]) self.assertEqual( factory.settings.y, [72.31, 86.03, 85.26, 81.11, 131.59, 95.36, 112.88, 80.55, 78.34]) self.assertEqual(factory.settings.data_defined_title, 'my_title_10') self.assertEqual(factory.settings.data_defined_legend_title, 'my_legend_10') self.assertEqual(factory.settings.data_defined_x_title, 'my_x_axis_10') self.assertEqual(factory.settings.data_defined_y_title, 'my_y_axis_10') self.assertEqual(factory.settings.data_defined_z_title, 'my_z_axis_10') self.assertEqual(factory.settings.data_defined_x_min, -10) self.assertEqual(factory.settings.data_defined_x_max, 10) self.assertEqual(factory.settings.data_defined_y_min, -10) self.assertEqual(factory.settings.data_defined_y_max, 10)