def __init__(self, style={}): """ Instantiate styling dictionary. This will be serialized to a JSON object and passed to OpenLayers. """ self.style = style settings = QSettings() # Use QGIS selection fill color qgs_sel_red = settings.value("/Qgis/default_selection_color_red", 255, type=int) qgs_sel_green = settings.value("/Qgis/default_selection_color_green", 255, type=int) qgs_sel_blue = settings.value("/Qgis/default_selection_color_blue", 0, type=int) qgs_sel_alpha = settings.value("/Qgis/default_selection_color_alpha", 255, type=int) sel_color = QColor(qgs_sel_red, qgs_sel_green, qgs_sel_blue, qgs_sel_alpha) # Set defaults self.style["fillColor"] = sel_color.name() self.style["fillOpacity"] = 0.5 self.style["strokeColor"] = "#FE2E64" self.style["strokeOpacity"] = 1 self.style["strokeWidth"] = 1
def table_item_clicked(self, tableItem): if tableItem.text() == "none": return # set color if tableItem.column() == 1: remember_color = tableItem.background().color() remember_color = QColor("white") if remember_color.name( ) == QColor("black").name() else remember_color color = QColorDialog.getColor(remember_color, self) if color.isValid(): tableItem.setBackground(color) self.tableBtnsConfig.clearSelection() # set the thematic class if tableItem.column() == 2: thematic_raster_class = ThematicRasterClasses() if thematic_raster_class.exec_(): tableItem.setText(thematic_raster_class.pix_value) self.tableBtnsConfig.item(tableItem.row(), 1).setBackground( thematic_raster_class.color) # clean the current row clicked in the trash icon if tableItem.column() == 3: self.tableBtnsConfig.item(tableItem.row(), 0).setText("") self.tableBtnsConfig.item(tableItem.row(), 1).setBackground( QColor(255, 255, 255, 0)) if not self.tableBtnsConfig.item(tableItem.row(), 2).text() == "none": self.tableBtnsConfig.item(tableItem.row(), 2).setText("")
def fetch_values_from_layer(self): # pylint: disable=too-many-locals, too-many-branches, too-many-statements """ (Re)fetches plot values from the source layer. """ # Note: we keep things nice and efficient and only iterate a single time over the layer! if not self.context_generator: context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes( self.source_layer)) else: context = self.context_generator.createExpressionContext() # add a new scope corresponding to the source layer -- this will potentially overwrite any other # layer scopes which may be present in the context (e.g. from atlas layers), but we need to ensure # that source layer fields and attributes are present in the context context.appendScope( self.source_layer.createExpressionContextScope()) self.settings.data_defined_properties.prepare(context) self.fetch_layout_properties(context) def add_source_field_or_expression(field_or_expression): field_index = self.source_layer.fields().lookupField( field_or_expression) if field_index == -1: expression = QgsExpression(field_or_expression) if not expression.hasParserError(): expression.prepare(context) return expression, expression.needsGeometry( ), expression.referencedColumns() return None, False, {field_or_expression} x_expression, x_needs_geom, x_attrs = add_source_field_or_expression(self.settings.properties['x_name']) if \ self.settings.properties[ 'x_name'] else (None, False, set()) y_expression, y_needs_geom, y_attrs = add_source_field_or_expression(self.settings.properties['y_name']) if \ self.settings.properties[ 'y_name'] else (None, False, set()) z_expression, z_needs_geom, z_attrs = add_source_field_or_expression(self.settings.properties['z_name']) if \ self.settings.properties[ 'z_name'] else (None, False, set()) additional_info_expression, additional_needs_geom, additional_attrs = add_source_field_or_expression( self.settings.layout['additional_info_expression'] ) if self.settings.layout['additional_info_expression'] else (None, False, set()) attrs = set().union( self.settings.data_defined_properties.referencedFields(), x_attrs, y_attrs, z_attrs, additional_attrs) request = QgsFeatureRequest() if self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).isActive(): expression = self.settings.data_defined_properties.property( PlotSettings.PROPERTY_FILTER).asExpression() request.setFilterExpression(expression) request.setExpressionContext(context) request.setSubsetOfAttributes(attrs, self.source_layer.fields()) if not x_needs_geom and not y_needs_geom and not z_needs_geom and not additional_needs_geom and not self.settings.data_defined_properties.hasActiveProperties( ): request.setFlags(QgsFeatureRequest.NoGeometry) visible_geom_engine = None if self.visible_features_only and self.visible_region is not None: ct = QgsCoordinateTransform( self.visible_region.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox(self.visible_region) request.setFilterRect(rect) except QgsCsException: pass elif self.visible_features_only and self.polygon_filter is not None: ct = QgsCoordinateTransform( self.polygon_filter.crs(), self.source_layer.crs(), QgsProject.instance().transformContext()) try: rect = ct.transformBoundingBox( self.polygon_filter.geometry.boundingBox()) request.setFilterRect(rect) g = self.polygon_filter.geometry g.transform(ct) visible_geom_engine = QgsGeometry.createGeometryEngine( g.constGet()) visible_geom_engine.prepareGeometry() except QgsCsException: pass if self.selected_features_only: it = self.source_layer.getSelectedFeatures(request) else: it = self.source_layer.getFeatures(request) # Some plot types don't draw individual glyphs for each feature, but aggregate them instead. # In that case it doesn't make sense to evaluate expressions for settings like marker size or color for each # feature. Instead, the evaluation should be executed only once for these settings. aggregating = self.settings.plot_type in ['box', 'histogram'] executed = False xx = [] yy = [] zz = [] additional_hover_text = [] marker_sizes = [] colors = [] stroke_colors = [] stroke_widths = [] for f in it: if visible_geom_engine and not visible_geom_engine.intersects( f.geometry().constGet()): continue self.settings.feature_ids.append(f.id()) context.setFeature(f) x = None if x_expression: x = x_expression.evaluate(context) if x == NULL or x is None: continue elif self.settings.properties['x_name']: x = f[self.settings.properties['x_name']] if x == NULL or x is None: continue y = None if y_expression: y = y_expression.evaluate(context) if y == NULL or y is None: continue elif self.settings.properties['y_name']: y = f[self.settings.properties['y_name']] if y == NULL or y is None: continue z = None if z_expression: z = z_expression.evaluate(context) if z == NULL or z is None: continue elif self.settings.properties['z_name']: z = f[self.settings.properties['z_name']] if z == NULL or z is None: continue if additional_info_expression: additional_hover_text.append( additional_info_expression.evaluate(context)) elif self.settings.layout['additional_info_expression']: additional_hover_text.append( f[self.settings.layout['additional_info_expression']]) if x is not None: xx.append(x) if y is not None: yy.append(y) if z is not None: zz.append(z) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_MARKER_SIZE): default_value = self.settings.properties['marker_size'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_MARKER_SIZE, context, default_value) marker_sizes.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_WIDTH): default_value = self.settings.properties['marker_width'] context.setOriginalValueVariable(default_value) value, _ = self.settings.data_defined_properties.valueAsDouble( PlotSettings.PROPERTY_STROKE_WIDTH, context, default_value) stroke_widths.append(value) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['in_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead colors.append(default_value.name()) if self.settings.data_defined_properties.isActive( PlotSettings.PROPERTY_STROKE_COLOR) and (not aggregating or not executed): default_value = QColor(self.settings.properties['out_color']) value, conversion_success = self.settings.data_defined_properties.valueAsColor( PlotSettings.PROPERTY_STROKE_COLOR, context, default_value) if conversion_success: # We were given a valid color specification, use that color stroke_colors.append(value.name()) else: try: # Attempt to interpret the value as a list of color specifications value_list = self.settings.data_defined_properties.value( PlotSettings.PROPERTY_STROKE_COLOR, context) color_list = [ QgsSymbolLayerUtils.decodeColor(item).name() for item in value_list ] stroke_colors.extend(color_list) except TypeError: # Not a list of color specifications, use the default color instead stroke_colors.append(default_value.name()) executed = True self.settings.additional_hover_text = additional_hover_text self.settings.x = xx self.settings.y = yy self.settings.z = zz if marker_sizes: self.settings.data_defined_marker_sizes = marker_sizes if colors: self.settings.data_defined_colors = colors if stroke_colors: self.settings.data_defined_stroke_colors = stroke_colors if stroke_widths: self.settings.data_defined_stroke_widths = stroke_widths
class GraphSettings_Dlg(QDialog, Ui_Dialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.titleBoldBtn.hide() self.titleItalicBtn.hide() self.labelsBoldBtn.hide() self.labelsItalicBtn.hide() # remove fonts matplotlib doesn't load for index in range(self.titleFontCombo.count() - 1, -1, -1): found = False family = unicode(self.titleFontCombo.itemText(index)) try: props = self.findFont({'family': family}) if family == props['family']: found = True except Exception: pass if not found: self.titleFontCombo.removeItem(index) self.labelsFontCombo.removeItem(index) self.initProps() self.titleColorBtn.clicked.connect(self.chooseTitleColor) self.labelsColorBtn.clicked.connect(self.chooseLabelsColor) self.pointsColorBtn.clicked.connect(self.choosePointsColor) self.pointsReplicasColorBtn.clicked.connect( self.choosePointsReplicasColor) self.linesColorBtn.clicked.connect(self.chooseLinesColor) self.linesThrendColorBtn.clicked.connect(self.chooseLinesThrendColor) def choosePointsColor(self): dlg = QColorDialog(self.pointsColor, self) if dlg.exec_(): self.setPointsColor(dlg.selectedColor()) dlg.deleteLater() def setPointsColor(self, color): self.pointsColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.pointsColor) self.pointsColorBtn.setIcon(QIcon(pixmap)) def pointsProps(self): props = {'marker': 's', 'color': self.pointsColor.name()} return props def setPointsProps(self, props): self.setPointsColor(props.get('color', 'black')) def choosePointsReplicasColor(self): dlg = QColorDialog(self.pointsReplicasColor, self) if dlg.exec_(): self.setPointsReplicasColor(dlg.selectedColor()) dlg.deleteLater() def setPointsReplicasColor(self, color): self.pointsReplicasColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.pointsReplicasColor) self.pointsReplicasColorBtn.setIcon(QIcon(pixmap)) def pointsReplicasProps(self): props = { 'marker': 's', 'color': self.pointsReplicasColor.name(), } return props def setPointsReplicasProps(self, props): self.setPointsReplicasColor(props.get('color', 'blue')) def chooseLinesColor(self): dlg = QColorDialog(self.linesColor, self) if dlg.exec_(): self.setLinesColor(dlg.selectedColor()) dlg.deleteLater() def setLinesColor(self, color): self.linesColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.linesColor) self.linesColorBtn.setIcon(QIcon(pixmap)) def linesProps(self): props = {'color': self.linesColor.name()} return props def setLinesProps(self, props): self.setLinesColor(props.get('color', 'black')) def chooseLinesThrendColor(self): dlg = QColorDialog(self.linesThrendColor, self) if dlg.exec_(): self.setLinesThrendColor(dlg.selectedColor()) dlg.deleteLater() def setLinesThrendColor(self, color): self.linesThrendColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.linesThrendColor) self.linesThrendColorBtn.setIcon(QIcon(pixmap)) def linesThrendProps(self): props = { 'color': self.linesThrendColor.name(), } return props def setLinesThrendProps(self, props): self.setLinesThrendColor(props.get('color', 'red')) def chooseTitleColor(self): dlg = QColorDialog(self.titleColor, self) if dlg.exec_(): self.setTitleColor(dlg.selectedColor()) dlg.deleteLater() def setTitleColor(self, color): self.titleColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.titleColor) self.titleColorBtn.setIcon(QIcon(pixmap)) def chooseLabelsColor(self): dlg = QColorDialog(self.labelsColor, self) if dlg.exec_(): self.setLabelsColor(dlg.selectedColor()) dlg.deleteLater() def setLabelsColor(self, color): self.labelsColor = QColor(color) pixmap = QPixmap(20, 20) pixmap.fill(self.labelsColor) self.labelsColorBtn.setIcon(QIcon(pixmap)) def titleFontProps(self): qfont = self.titleFontCombo.currentFont() qfont.setPointSize(self.titleSizeSpin.value()) if self.titleBoldBtn.isVisible() and self.titleBoldBtn.isChecked(): qfont.setBold(True) if self.titleItalicBtn.isVisible() and self.titleItalicBtn.isChecked(): qfont.setItalic(True) # search for the best match props = self.qfontToProps(qfont) props['color'] = self.titleColor.name() props['size'] = self.titleSizeSpin.value() return props def setTitleFontProps(self, props): if 'family' in props: index = self.titleFontCombo.findText(props['family']) if index >= 0: self.titleFontCombo.setCurrentIndex(index) if 'size' in props: try: self.titleSizeSpin.setValue(int(props['size'])) except ValueError: pass self.setTitleColor(props.get('color', 'black')) self.titleBoldBtn.setChecked(props.get('weight', '') == 'bold') self.titleItalicBtn.setChecked(props.get('style', '') == 'italic') def labelsFontProps(self): qfont = self.labelsFontCombo.currentFont() qfont.setPointSize(self.titleSizeSpin.value()) if self.labelsBoldBtn.isVisible() and self.labelsBoldBtn.isChecked(): qfont.setBold(True) if self.labelsItalicBtn.isVisible() and self.labelsItalicBtn.isChecked( ): qfont.setItalic(True) # search for the best match props = self.qfontToProps(qfont) props['color'] = self.labelsColor.name() props['size'] = self.labelsSizeSpin.value() return props def setLabelsFontProps(self, props): if 'family' in props: index = self.labelsFontCombo.findText(props['family']) if index >= 0: self.labelsFontCombo.setCurrentIndex(index) if 'size' in props: try: self.labelsSizeSpin.setValue(int(props['size'])) except ValueError: pass self.setLabelsColor(props.get('color', 'black')) self.labelsBoldBtn.setChecked(props.get('weight', '') == 'bold') self.labelsItalicBtn.setChecked(props.get('style', '') == 'italic') def initProps(self): settings = QgsSettings() self.setTitleFontProps( self.settingsToDict(settings.value("/pstimeseries/titleProps", {}))) self.setLabelsFontProps( self.settingsToDict(settings.value("/pstimeseries/labelsProps", {}))) self.setPointsProps( self.settingsToDict(settings.value("/pstimeseries/pointsProps", {}))) self.setPointsReplicasProps( self.settingsToDict( settings.value("/pstimeseries/pointsReplicasProps", {}))) self.setLinesProps( self.settingsToDict(settings.value("/pstimeseries/linesProps", {}))) self.setLinesThrendProps( self.settingsToDict( settings.value("/pstimeseries/linesThrendProps", {}))) def accept(self): settings = QgsSettings() settings.setValue("/pstimeseries/titleProps", self.titleFontProps()) settings.setValue("/pstimeseries/labelsProps", self.labelsFontProps()) settings.setValue("/pstimeseries/pointsProps", self.pointsProps()) settings.setValue("/pstimeseries/pointsReplicasProps", self.pointsReplicasProps()) settings.setValue("/pstimeseries/linesProps", self.linesProps()) settings.setValue("/pstimeseries/linesThrendProps", self.linesThrendProps()) QDialog.accept(self) @classmethod def settingsToDict(self, s): r = {} for k, v in s.items(): r[unicode(k)] = unicode(v) if isinstance(v, str) else v return r @classmethod def qfontToProps(self, qfont): props = { 'family': unicode(qfont.family()), 'stretch': qfont.stretch(), 'weight': qfont.weight() } if qfont.style() == QFont.StyleItalic: style = 'italic' elif qfont.style() == QFont.StyleOblique: style = 'oblique' else: style = 'normal' props['style'] = style if qfont.capitalization() == QFont.SmallCaps: props['variant'] = 'small-caps' return self.findFont(props) @classmethod def findFont(self, props): from matplotlib.font_manager import FontProperties, findfont # search for the best match font = FontProperties(fname=findfont(FontProperties(**props))) props = { 'family': font.get_name(), 'weight': font.get_weight(), 'style': font.get_style(), } return props
class GraphSettings_Dlg(QDialog, Ui_Dialog): def __init__(self, parent=None): QDialog.__init__(self, parent) self.setupUi(self) self.titleBoldBtn.hide() self.titleItalicBtn.hide() self.labelsBoldBtn.hide() self.labelsItalicBtn.hide() # remove fonts matplotlib doesn't load for index in range(self.titleFontCombo.count()-1, -1, -1): found = False family = unicode( self.titleFontCombo.itemText( index ) ) try: props = self.findFont( {'family':family} ) if family == props['family']: found = True except Exception: pass if not found: self.titleFontCombo.removeItem( index ) self.labelsFontCombo.removeItem( index ) self.initProps() self.titleColorBtn.clicked.connect(self.chooseTitleColor) self.labelsColorBtn.clicked.connect(self.chooseLabelsColor) self.pointsColorBtn.clicked.connect(self.choosePointsColor) self.pointsReplicasColorBtn.clicked.connect(self.choosePointsReplicasColor) self.linesColorBtn.clicked.connect(self.chooseLinesColor) self.linesThrendColorBtn.clicked.connect(self.chooseLinesThrendColor) def choosePointsColor(self): dlg = QColorDialog(self.pointsColor, self) if dlg.exec_(): self.setPointsColor( dlg.selectedColor() ) dlg.deleteLater() def setPointsColor(self, color): self.pointsColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.pointsColor ) self.pointsColorBtn.setIcon( QIcon(pixmap) ) def pointsProps(self): props = { 'marker' : 's', 'color' : self.pointsColor.name() } return props def setPointsProps(self, props): self.setPointsColor( props.get('color', 'black') ) def choosePointsReplicasColor(self): dlg = QColorDialog(self.pointsReplicasColor, self) if dlg.exec_(): self.setPointsReplicasColor( dlg.selectedColor() ) dlg.deleteLater() def setPointsReplicasColor(self, color): self.pointsReplicasColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.pointsReplicasColor ) self.pointsReplicasColorBtn.setIcon( QIcon(pixmap) ) def pointsReplicasProps(self): props = { 'marker' : 's', 'color' : self.pointsReplicasColor.name(), } return props def setPointsReplicasProps(self, props): self.setPointsReplicasColor( props.get('color', 'blue') ) def chooseLinesColor(self): dlg = QColorDialog(self.linesColor, self) if dlg.exec_(): self.setLinesColor( dlg.selectedColor() ) dlg.deleteLater() def setLinesColor(self, color): self.linesColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.linesColor ) self.linesColorBtn.setIcon( QIcon(pixmap) ) def linesProps(self): props = { 'color' : self.linesColor.name() } return props def setLinesProps(self, props): self.setLinesColor( props.get('color', 'black') ) def chooseLinesThrendColor(self): dlg = QColorDialog(self.linesThrendColor, self) if dlg.exec_(): self.setLinesThrendColor( dlg.selectedColor() ) dlg.deleteLater() def setLinesThrendColor(self, color): self.linesThrendColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.linesThrendColor ) self.linesThrendColorBtn.setIcon( QIcon(pixmap) ) def linesThrendProps(self): props = { 'color' : self.linesThrendColor.name(), } return props def setLinesThrendProps(self, props): self.setLinesThrendColor( props.get('color', 'red') ) def chooseTitleColor(self): dlg = QColorDialog(self.titleColor, self) if dlg.exec_(): self.setTitleColor( dlg.selectedColor() ) dlg.deleteLater() def setTitleColor(self, color): self.titleColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.titleColor ) self.titleColorBtn.setIcon( QIcon(pixmap) ) def chooseLabelsColor(self): dlg = QColorDialog(self.labelsColor, self) if dlg.exec_(): self.setLabelsColor( dlg.selectedColor() ) dlg.deleteLater() def setLabelsColor(self, color): self.labelsColor = QColor( color ) pixmap = QPixmap( 20,20 ) pixmap.fill( self.labelsColor ) self.labelsColorBtn.setIcon( QIcon(pixmap) ) def titleFontProps(self): qfont = self.titleFontCombo.currentFont() qfont.setPointSize( self.titleSizeSpin.value() ) if self.titleBoldBtn.isVisible() and self.titleBoldBtn.isChecked(): qfont.setBold( True ) if self.titleItalicBtn.isVisible() and self.titleItalicBtn.isChecked(): qfont.setItalic( True ) # search for the best match props = self.qfontToProps( qfont ) props['color'] = self.titleColor.name() props['size'] = self.titleSizeSpin.value() return props def setTitleFontProps(self, props): if 'family' in props: index = self.titleFontCombo.findText( props['family'] ) if index >= 0: self.titleFontCombo.setCurrentIndex( index ) if 'size' in props: try: self.titleSizeSpin.setValue( int(props['size']) ) except ValueError: pass self.setTitleColor( props.get('color', 'black') ) self.titleBoldBtn.setChecked( props.get('weight', '') == 'bold' ) self.titleItalicBtn.setChecked( props.get('style', '') == 'italic' ) def labelsFontProps(self): qfont = self.labelsFontCombo.currentFont() qfont.setPointSize( self.titleSizeSpin.value() ) if self.labelsBoldBtn.isVisible() and self.labelsBoldBtn.isChecked(): qfont.setBold( True ) if self.labelsItalicBtn.isVisible() and self.labelsItalicBtn.isChecked(): qfont.setItalic( True ) # search for the best match props = self.qfontToProps( qfont ) props['color'] = self.labelsColor.name() props['size'] = self.labelsSizeSpin.value() return props def setLabelsFontProps(self, props): if 'family' in props: index = self.labelsFontCombo.findText( props['family'] ) if index >= 0: self.labelsFontCombo.setCurrentIndex( index ) if 'size' in props: try: self.labelsSizeSpin.setValue( int(props['size']) ) except ValueError: pass self.setLabelsColor( props.get('color', 'black') ) self.labelsBoldBtn.setChecked( props.get('weight', '') == 'bold' ) self.labelsItalicBtn.setChecked( props.get('style', '') == 'italic' ) def initProps(self): settings = QgsSettings() self.setTitleFontProps( self.settingsToDict( settings.value("/pstimeseries/titleProps", {}) ) ) self.setLabelsFontProps( self.settingsToDict( settings.value("/pstimeseries/labelsProps", {}) ) ) self.setPointsProps( self.settingsToDict( settings.value("/pstimeseries/pointsProps", {}) ) ) self.setPointsReplicasProps( self.settingsToDict( settings.value("/pstimeseries/pointsReplicasProps", {}) ) ) self.setLinesProps( self.settingsToDict( settings.value("/pstimeseries/linesProps", {}) ) ) self.setLinesThrendProps( self.settingsToDict( settings.value("/pstimeseries/linesThrendProps", {}) ) ) def accept(self): settings = QgsSettings() settings.setValue("/pstimeseries/titleProps", self.titleFontProps()) settings.setValue("/pstimeseries/labelsProps", self.labelsFontProps()) settings.setValue("/pstimeseries/pointsProps", self.pointsProps()) settings.setValue("/pstimeseries/pointsReplicasProps", self.pointsReplicasProps()) settings.setValue("/pstimeseries/linesProps", self.linesProps()) settings.setValue("/pstimeseries/linesThrendProps", self.linesThrendProps()) QDialog.accept(self) @classmethod def settingsToDict(self, s): r = {} for k,v in s.items(): r[ unicode(k) ] = unicode(v) if isinstance(v, str) else v return r @classmethod def qfontToProps(self, qfont): props = { 'family' : unicode(qfont.family()), 'stretch' : qfont.stretch(), 'weight' : qfont.weight() } if qfont.style() == QFont.StyleItalic: style = 'italic' elif qfont.style() == QFont.StyleOblique: style = 'oblique' else: style = 'normal' props['style'] = style if qfont.capitalization() == QFont.SmallCaps: props['variant'] = 'small-caps' return self.findFont(props) @classmethod def findFont(self, props): from matplotlib.font_manager import FontProperties, findfont # search for the best match font = FontProperties( fname=findfont( FontProperties(**props) ) ) props = { 'family' : font.get_name(), 'weight' : font.get_weight(), 'style' : font.get_style(), } return props
class DistroMap(object): def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisSettingsDirPath()).path() + "/python/plugins/distromap" # initialize locale localePath = "" locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/distromap_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = DistroMapDialog() def confirm(self): # runs when OK button is pressed # initialise input parameters self.BASE_LAYER = self.dlg.ui.comboBase.currentItemData() self.SECONDARY_LAYER = self.dlg.ui.comboSecondary.currentItemData() self.SURFACE_LAYER = self.dlg.ui.comboSurface.currentItemData() self.LOCALITIES_LAYER = self.dlg.ui.comboLocalities.currentItemData() self.TAXON_FIELD_INDEX = self.dlg.ui.comboTaxonField.currentItemData()[0] self.GRID_LAYER = self.dlg.ui.comboGrid.currentItemData() self.X_MIN = float(self.dlg.ui.leMinX.text()) self.Y_MIN = float(self.dlg.ui.leMinY.text()) self.X_MAX = float(self.dlg.ui.leMaxX.text()) self.Y_MAX = float(self.dlg.ui.leMaxY.text()) self.OUT_WIDTH = self.dlg.ui.spnOutWidth.value() self.OUT_HEIGHT = self.dlg.ui.spnOutHeight.value() self.OUT_DIR = self.dlg.ui.leOutDir.text() try: self.getUniqueValues() except: message = "Could not get unique values from localities layer. " message += "Check that the localities layer and taxon identifier " message += "field are properly specified." QMessageBox.information(self.dlg,"Distribution Map Generator", message) return question = "This will generate " + str(self.UNIQUE_COUNT) question += " maps. Are you sure you want to continue?" reply = QMessageBox.question(self.dlg,'Distribution Map Generator', question, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) self.GRID_INDEX = QgsSpatialIndex() for feat in getLayerFromId(self.GRID_LAYER).getFeatures(): self.GRID_INDEX.insertFeature(feat) if reply == QMessageBox.Yes: try: self.process() except QgsCsException: return except Exception as ex: log(ex) return QMessageBox.information(self.dlg,"Distribution Map Generator", "Map processing complete.") self.dlg.ui.progressBar.setValue(0) QDialog.accept(self.dlg) else: return def initGui(self): # Create action that will start plugin configuration self.action = QAction( QIcon(self.plugin_dir + "/icon.svg"), u"Distribution Map Generator...", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) self.dlg.ui.buttonBox.accepted.connect(self.confirm) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Distribution Map Generator", self.action) # Set colour for output background colour chooser: self.BACKGROUND_COLOUR = QColor(192,192,255) self.dlg.ui.frmColour.setStyleSheet("QWidget { background-color: %s }" % self.BACKGROUND_COLOUR.name()) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Distribution Map Generator", self.action) self.iface.removeToolBarIcon(self.action) def loadTaxonFields(self): self.dlg.ui.comboTaxonField.clear() try: layer=getLayerFromId(self.dlg.ui.comboLocalities.currentItemData()) provider=layer.dataProvider() except AttributeError: #Crashes without valid shapefiles log("Could not access the localities layer. Is it a valid vector layer?") return try: fieldmap=provider.fieldNameMap() for (name,index) in fieldmap.items(): self.dlg.ui.comboTaxonField.addItem(name,index) except: log("Could not load the field names for the localities layer.") def loadOutDir(self): #newname = QFileDialog.getExistingDirectory(None, "Output Maps Directory", self.dlg.ui.leOutDir.displayText()) newname = QFileDialog.getExistingDirectory(None, "Output Maps Directory") if newname != None: self.dlg.ui.leOutDir.setText(newname) def getCurrentExtent(self): extent = self.iface.mapCanvas().extent() # {"{0:.6f}".format() shows the float with 6 decimal places self.dlg.ui.leMinX.setText(str("{0:.6f}".format(extent.xMinimum()))) self.dlg.ui.leMinY.setText(str("{0:.6f}".format(extent.yMinimum()))) self.dlg.ui.leMaxX.setText(str("{0:.6f}".format(extent.xMaximum()))) self.dlg.ui.leMaxY.setText(str("{0:.6f}".format(extent.yMaximum()))) def getUniqueValues(self): layer = getLayerFromId(self.LOCALITIES_LAYER) self.UNIQUE_VALUES = layer.dataProvider().uniqueValues(int(self.TAXON_FIELD_INDEX)) self.UNIQUE_COUNT = len(self.UNIQUE_VALUES) def selectByAttribute(self, value): layer = getLayerFromId(self.LOCALITIES_LAYER) field_index = self.TAXON_FIELD_INDEX field_name = layer.fields()[int(field_index)].name() selected = [] filter = QgsExpression.createFieldEqualityExpression(field_name, str(value)) request = QgsFeatureRequest().setFilterExpression(filter) request.setSubsetOfAttributes([]) for feature in layer.getFeatures(request): selected.append(feature.id()) layer.selectByIds(selected) def selectByLocation(self): gridLayer = getLayerFromId(self.GRID_LAYER) selectLayer = getLayerFromId(self.LOCALITIES_LAYER) if gridLayer.crs() != selectLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "Localities layer and grid layers must have the same projection.") raise QgsCsException("Localities layer and grid layers must have the same projection.") selectedSet = [] feats = selectLayer.selectedFeatures() for f in feats: geom = QgsGeometry(f.geometry()) intersects = self.GRID_INDEX.intersects(geom.boundingBox()) for i in intersects: request = QgsFeatureRequest().setFilterFid(i) feat = next(gridLayer.getFeatures(request)) tmpGeom = QgsGeometry( feat.geometry() ) if geom.intersects(tmpGeom): selectedSet.append(feat.id()) gridLayer.selectByIds(selectedSet) def saveSelected(self): gridLayer = getLayerFromId(self.GRID_LAYER) # create memory layer outputLayer = QgsVectorLayer("Polygon", "taxon", "memory") outProvider = outputLayer.dataProvider() # add features outGrids = gridLayer.selectedFeatures() for grid in outGrids: outProvider.addFeatures([grid]) outputLayer.updateExtents() self.TAXON_GRID_LAYER = outputLayer def setBackgroundColour(self): col = QColorDialog.getColor() if col.isValid(): self.BACKGROUND_COLOUR = col self.dlg.ui.frmColour.setStyleSheet("QWidget { background-color: %s }" % self.BACKGROUND_COLOUR.name()) def printMap(self,taxon): # copy style from grid layer to output layer outstyle = tempfile.gettempdir() + os.sep + "output.qml" getLayerFromId(self.GRID_LAYER).saveNamedStyle(outstyle) self.TAXON_GRID_LAYER.loadNamedStyle(outstyle) # create layer set baseLayer = getLayerFromId(self.BASE_LAYER) if self.TAXON_GRID_LAYER.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") baseCrs = baseLayer.crs() if self.SECONDARY_LAYER != "None": secondaryLayer = getLayerFromId(self.SECONDARY_LAYER) if secondaryLayer.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") else: secondaryLayer = None if self.SURFACE_LAYER != "None": surfaceLayer = getLayerFromId(self.SURFACE_LAYER) if surfaceLayer.crs() != baseLayer.crs(): QMessageBox.information(self.dlg,"Distribution Map Generator", "All layers must have the same projection.") raise QgsCsException("All layers must have the same projection.") else: surfaceLayer = None lst = [] lst.append(self.TAXON_GRID_LAYER) if self.SURFACE_LAYER != "None": lst.append(surfaceLayer) if self.SECONDARY_LAYER != "None": lst.append(secondaryLayer) lst.append(baseLayer) ms = QgsMapSettings() ms.setLayers(lst) ms.setBackgroundColor(self.BACKGROUND_COLOUR) # set extent (xmin,ymin,xmax,ymax) rect = QgsRectangle(self.X_MIN,self.Y_MIN,self.X_MAX,self.Y_MAX) ms.setExtent(rect) # set output size outputSize = QSize(self.OUT_WIDTH,self.OUT_HEIGHT) ms.setOutputSize(outputSize) # create painter p = QPainter() p.setRenderHint(QPainter.Antialiasing) # create image (dimensions 325x299) img = QImage(outputSize, QImage.Format_ARGB32_Premultiplied) p.begin(img) # do the rendering r = QgsMapRendererCustomPainterJob(ms, p) r.start() r.waitForFinished() p.end() # save image outdir = self.OUT_DIR img.save(outdir+os.sep+str(str(taxon))+".png","png") def process(self): self.dlg.ui.progressBar.setMaximum(len(self.UNIQUE_VALUES)) # process all unique taxa getLayerFromId(self.LOCALITIES_LAYER).selectByIds([]) # use global projection #oldValidation = QSettings().value( "/Projections/defaultBehavior", "useGlobal", type=str ) #QSettings().setValue( "/Projections/defaultBehavior", "useGlobal" ) for taxon in self.UNIQUE_VALUES: self.selectByAttribute(taxon) self.selectByLocation() self.saveSelected() #load newly created memory layer QgsProject.instance().addMapLayer(self.TAXON_GRID_LAYER) try: self.printMap(taxon) except QgsCsException: #unload memory layer QgsProject.instance().removeMapLayers([self.TAXON_GRID_LAYER.id()]) self.TAXON_GRID_LAYER = None getLayerFromId(self.LOCALITIES_LAYER).removeSelection() getLayerFromId(self.GRID_LAYER).removeSelection() raise #unload memory layer QgsProject.instance().removeMapLayers([self.TAXON_GRID_LAYER.id()]) self.TAXON_GRID_LAYER = None self.dlg.ui.progressBar.setValue(self.dlg.ui.progressBar.value()+1) #restore saved default projection setting #QSettings().setValue( "/Projections/defaultBehaviour", oldValidation ) #clear selection getLayerFromId(self.LOCALITIES_LAYER).removeSelection() getLayerFromId(self.GRID_LAYER).removeSelection() # run method that performs all the real work def run(self): # first clear combo boxes so they don't get duplicate entries: self.dlg.ui.comboBase.clear() self.dlg.ui.comboSecondary.clear() self.dlg.ui.comboSurface.clear() self.dlg.ui.comboLocalities.clear() self.dlg.ui.comboGrid.clear() # populate combo boxes: self.dlg.ui.comboSecondary.addItem("None",None) self.dlg.ui.comboSurface.addItem("None",None) for layer in self.iface.mapCanvas().layers(): self.dlg.ui.comboBase.addItem(layer.name(),layer.id()) self.dlg.ui.comboSecondary.addItem(layer.name(),layer.id()) self.dlg.ui.comboSurface.addItem(layer.name(),layer.id()) #vector only layers: if type(layer).__name__ == "QgsVectorLayer": self.dlg.ui.comboLocalities.addItem(layer.name(),layer.id()) self.dlg.ui.comboGrid.addItem(layer.name(),layer.id()) self.loadTaxonFields() # define the signal connectors #QObject.connect(self.dlg.ui.comboLocalities,SIGNAL('currentIndexChanged (int)'),self.loadTaxonFields) self.dlg.ui.comboLocalities.currentIndexChanged.connect(self.loadTaxonFields) #QObject.connect(self.dlg.ui.btnBrowse,SIGNAL('clicked()'),self.loadOutDir) self.dlg.ui.btnBrowse.clicked.connect(self.loadOutDir) #QObject.connect(self.dlg.ui.btnExtent,SIGNAL('clicked()'),self.getCurrentExtent) self.dlg.ui.btnExtent.clicked.connect(self.getCurrentExtent) #QObject.connect(self.dlg.ui.btnColour,SIGNAL('clicked()'),self.setBackgroundColour) self.dlg.ui.btnColour.clicked.connect(self.setBackgroundColour) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_()