1# -*- coding: utf-8 -*-
2
3"""
4***************************************************************************
5    BarPlot.py
6    ---------------------
7    Date                 : March 2015
8    Copyright            : (C) 2017 by Matteo Ghetta
9    Email                : matteo dot ghetta at gmail dot com
10***************************************************************************
11*                                                                         *
12*   This program is free software; you can redistribute it and/or modify  *
13*   it under the terms of the GNU General Public License as published by  *
14*   the Free Software Foundation; either version 2 of the License, or     *
15*   (at your option) any later version.                                   *
16*                                                                         *
17***************************************************************************
18"""
19
20__author__ = 'Matteo Ghetta'
21__date__ = 'March 2017'
22__copyright__ = '(C) 2017, Matteo Ghetta'
23
24import warnings
25
26from qgis.core import (QgsProcessingException,
27                       QgsProcessingParameterFeatureSource,
28                       QgsProcessingParameterField,
29                       QgsProcessingParameterEnum,
30                       QgsProcessingParameterFileDestination,
31                       QgsFeatureRequest)
32from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
33from processing.tools import vector
34
35from qgis.PyQt.QtCore import QCoreApplication
36
37
38class BoxPlot(QgisAlgorithm):
39    INPUT = 'INPUT'
40    OUTPUT = 'OUTPUT'
41    NAME_FIELD = 'NAME_FIELD'
42    VALUE_FIELD = 'VALUE_FIELD'
43    MSD = 'MSD'
44
45    def group(self):
46        return self.tr('Plots')
47
48    def groupId(self):
49        return 'plots'
50
51    def __init__(self):
52        super().__init__()
53
54    def initAlgorithm(self, config=None):
55        self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
56                                                              self.tr('Input layer')))
57        self.addParameter(QgsProcessingParameterField(self.NAME_FIELD,
58                                                      self.tr('Category name field'),
59                                                      parentLayerParameterName=self.INPUT,
60                                                      type=QgsProcessingParameterField.Any))
61        self.addParameter(QgsProcessingParameterField(self.VALUE_FIELD,
62                                                      self.tr('Value field'),
63                                                      parentLayerParameterName=self.INPUT,
64                                                      type=QgsProcessingParameterField.Numeric))
65        msd = [self.tr('Show Mean'),
66               self.tr('Show Standard Deviation'),
67               self.tr('Don\'t show Mean and Standard Deviation')
68               ]
69        self.addParameter(QgsProcessingParameterEnum(
70            self.MSD,
71            self.tr('Additional Statistic Lines'),
72            options=msd, defaultValue=0))
73
74        self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Box plot'), self.tr('HTML files (*.html)')))
75
76    def name(self):
77        return 'boxplot'
78
79    def displayName(self):
80        return self.tr('Box plot')
81
82    def processAlgorithm(self, parameters, context, feedback):
83        try:
84            # importing plotly throws Python warnings from within the library - filter these out
85            with warnings.catch_warnings():
86                warnings.filterwarnings("ignore", category=ResourceWarning)
87                warnings.filterwarnings("ignore", category=ImportWarning)
88                import plotly as plt
89                import plotly.graph_objs as go
90        except ImportError:
91            raise QgsProcessingException(QCoreApplication.translate('BoxPlot', 'This algorithm requires the Python “plotly” library. Please install this library and try again.'))
92
93        source = self.parameterAsSource(parameters, self.INPUT, context)
94        if source is None:
95            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
96
97        namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context)
98        valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context)
99
100        output = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
101
102        values = vector.values(source, valuefieldname)
103
104        x_index = source.fields().lookupField(namefieldname)
105        x_var = vector.convert_nulls([i[namefieldname] for i in source.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([x_index]))], '<NULL>')
106
107        msdIndex = self.parameterAsEnum(parameters, self.MSD, context)
108        msd = True
109
110        if msdIndex == 1:
111            msd = 'sd'
112        elif msdIndex == 2:
113            msd = False
114
115        data = [go.Box(
116                x=x_var,
117                y=values[valuefieldname],
118                boxmean=msd)]
119
120        plt.offline.plot(data, filename=output, auto_open=False)
121
122        return {self.OUTPUT: output}
123