Exemple #1
0
    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("")
Exemple #3
0
    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
Exemple #4
0
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
Exemple #6
0
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_()