예제 #1
0
class ValueWidget(QWidget, Ui_Widget):

    def __init__(self, iface):

        self.hasqwt=hasqwt
        self.hasmpl=hasmpl
        self.layerMap=dict()
        self.statsChecked=False
        self.ymin=0
        self.ymax=250

        self.iface=iface
        self.canvas=self.iface.mapCanvas()
        self.legend=self.iface.legendInterface()
        self.logger = logging.getLogger('.'.join((__name__, 
                                        self.__class__.__name__)))

        QWidget.__init__(self)
        self.setupUi(self)
        self.setupUi_extra()

        QObject.connect(self.cbxActive,SIGNAL("stateChanged(int)"),self.changeActive)
        QObject.connect(self.cbxGraph,SIGNAL("stateChanged(int)"),self.changePage)
        QObject.connect(self.canvas, SIGNAL( "keyPressed( QKeyEvent * )" ), self.pauseDisplay )
        QObject.connect(self.plotSelector, SIGNAL( "currentIndexChanged ( int )" ), self.changePlot )

    def setupUi_extra(self):

        # checkboxes
        #self.changeActive(Qt.Checked)
        #set inactive by default - should save last state in user config
        self.cbxActive.setCheckState(Qt.Unchecked)

        # plot
        self.plotSelector.setVisible( False )
        self.cbxStats.setVisible( False )
        self.graphControls.setVisible( False )
        if self.hasqwt:
            self.plotSelector.addItem( 'Qwt' )
        if self.hasmpl:
            self.plotSelector.addItem( 'mpl' )
        self.plotSelector.setCurrentIndex( 0 );
        if (not self.hasqwt or not self.hasmpl):
            #self.plotSelector.setVisible(False)
            self.plotSelector.setEnabled(False)

        # Page 2 - qwt
        if self.hasqwt:
            self.qwtPlot = QwtPlot(self.stackedWidget)
            self.qwtPlot.setAutoFillBackground(False)
            self.qwtPlot.setObjectName("qwtPlot")
            self.curve = QwtPlotCurve()
            self.curve.setSymbol(
                QwtSymbol(QwtSymbol.Ellipse,
                          QBrush(Qt.white),
                          QPen(Qt.red, 2),
                          QSize(9, 9)))
            self.curve.attach(self.qwtPlot)
            self.qwtPlot.setVisible(False)
        else:
            self.qwtPlot = QtGui.QLabel("Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.qwtPlot.sizePolicy().hasHeightForWidth())
        self.qwtPlot.setSizePolicy(sizePolicy)
        self.qwtPlot.setObjectName("qwtPlot")
        self.qwtPlot.updateGeometry()
        self.stackedWidget.addWidget(self.qwtPlot)

        #Page 3 - matplotlib
        self.mplLine = None #make sure to invalidate when layers change
        if self.hasmpl:
            # mpl stuff
            # should make figure light gray
            self.mplBackground = None #http://www.scipy.org/Cookbook/Matplotlib/Animations
            self.mplFig = plt.Figure(facecolor='w', edgecolor='w')
            self.mplFig.subplots_adjust(left=0.1, right=0.975, bottom=0.13, top=0.95)
            self.mplPlt = self.mplFig.add_subplot(111)   
            self.mplPlt.tick_params(axis='both', which='major', labelsize=12)
            self.mplPlt.tick_params(axis='both', which='minor', labelsize=10)                           
            # qt stuff
            self.pltCanvas = FigureCanvasQTAgg(self.mplFig)
            self.pltCanvas.setParent(self.stackedWidget)
            self.pltCanvas.setAutoFillBackground(False)
            self.pltCanvas.setObjectName("mplPlot")
            self.mplPlot = self.pltCanvas
            self.mplPlot.setVisible(False)
        else:
            self.mplPlot = QtGui.QLabel("Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.mplPlot.sizePolicy().hasHeightForWidth())
        self.mplPlot.setSizePolicy(sizePolicy)
        self.qwtPlot.setObjectName("qwtPlot")
        self.mplPlot.updateGeometry()
        self.stackedWidget.addWidget(self.mplPlot)

        self.stackedWidget.setCurrentIndex(0)


    def disconnect(self):
        self.changeActive(False)
        QObject.disconnect(self.canvas, SIGNAL( "keyPressed( QKeyEvent * )" ), self.pauseDisplay )
    
    def pauseDisplay(self,e):
      if ( e.modifiers() == Qt.ShiftModifier or e.modifiers() == Qt.MetaModifier ) and e.key() == Qt.Key_A:

        self.cbxActive.toggle()
        return True
      return False


    def keyPressEvent( self, e ):
      if ( e.modifiers() == Qt.ControlModifier or e.modifiers() == Qt.MetaModifier ) and e.key() == Qt.Key_C:
        items = QString()
        for rec in range( self.tableWidget.rowCount() ):
          items.append( '"' + self.tableWidget.item( rec, 0 ).text() + '",' + self.tableWidget.item( rec, 1 ).text() + "\n" )
        if not items.isEmpty():
          clipboard = QApplication.clipboard()
          clipboard.setText( items )
      elif (self.pauseDisplay(e)):
        pass
      else:
        QWidget.keyPressEvent( self, e )


    def changePage(self,state):
        if (state==Qt.Checked):
            self.plotSelector.setVisible( True )
            self.cbxStats.setVisible( True )
            self.graphControls.setVisible( True )
            if (self.plotSelector.currentText()=='mpl'):
                self.stackedWidget.setCurrentIndex(2)
            else:
                self.stackedWidget.setCurrentIndex(1)
        else:
            self.plotSelector.setVisible( False )
            self.cbxStats.setVisible( False )
            self.graphControls.setVisible( False )
            self.stackedWidget.setCurrentIndex(0)

    def changePlot(self):
        self.changePage(self.cbxActive.checkState())

    def changeActive(self,state):
        if (state==Qt.Checked):
            #QObject.connect(self.legend, SIGNAL( "itemAdded ( QModelIndex )" ), self.statsNeedChecked )
            #QObject.connect(self.legend, SIGNAL( "itemRemoved ()" ), self.invalidatePlot )
            QObject.connect(self.canvas, SIGNAL( "layersChanged ()" ), self.invalidatePlot )
            if int(QGis.QGIS_VERSION[2]) > 2: # for QGIS >= 1.3
                QObject.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint &)"), self.printValue)
            else:
                QObject.connect(self.canvas, SIGNAL("xyCoordinates(QgsPoint &)"), self.printValue)
        else:
            QObject.disconnect(self.canvas, SIGNAL( "layersChanged ()" ), self.invalidatePlot )
            if int(QGis.QGIS_VERSION[2]) > 2: # for QGIS >= 1.3
                QObject.disconnect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint &)"), self.printValue)
            else:
                QObject.disconnect(self.canvas, SIGNAL("xyCoordinates(QgsPoint &)"), self.printValue)


    def printValue(self,position):

        if self.canvas.layerCount() == 0:
            self.values=[]         
            self.showValues()
            return
        
        needextremum = self.cbxGraph.isChecked() # if plot is checked

        # count the number of requires rows and remember the raster layers
        nrow=0
        rasterlayers=[]
        layersWOStatistics=[]

        for i in range(self.canvas.layerCount()):
            layer = self.canvas.layer(i)
            if (layer!=None and layer.isValid() and layer.type()==QgsMapLayer.RasterLayer):
              if QGis.QGIS_VERSION_INT >= 10900: # for QGIS >= 1.9
                if not layer.dataProvider():
                  continue

                if not layer.dataProvider().capabilities() & QgsRasterDataProvider.IdentifyValue:
                  continue

                nrow+=layer.bandCount()
                rasterlayers.append(layer)

              else: # < 1.9
                if layer.providerKey()=="wms":
                  continue

                if layer.providerKey()=="grassraster":
                  nrow+=1
                  rasterlayers.append(layer)
                else: # normal raster layer
                  nrow+=layer.bandCount()
                  rasterlayers.append(layer)
                
              # check statistics for each band
              if needextremum:
                for i in range( 1,layer.bandCount()+1 ):
                  if int(QGis.QGIS_VERSION[2]) > 8: # for QGIS > 1.8
                    has_stats=layer.dataProvider().hasStatistics(i)
                  else:
                    has_stats=layer.hasStatistics(i)
                  if not layer.id() in self.layerMap and not has_stats\
                          and not layer in layersWOStatistics:
                    layersWOStatistics.append(layer)

        if layersWOStatistics and not self.statsChecked:
          self.calculateStatistics(layersWOStatistics)
                  
        # create the row if necessary
        self.tableWidget.setRowCount(nrow)

        irow=0
        self.values=[]
        self.ymin=1e38
        self.ymax=-1e38

        mapCanvasSrs = self.iface.mapCanvas().mapRenderer().destinationSrs()

        # TODO - calculate the min/max values only once, instead of every time!!!
        # keep them in a dict() with key=layer.id()
                
        for layer in rasterlayers:
            layername=unicode(layer.name())
            layerSrs = layer.srs()
            pos = position         

            # if given no position, get dummy values
            if position is None:
                pos = QgsPoint(0,0)
            # transform points if needed
            elif not mapCanvasSrs == layerSrs and self.iface.mapCanvas().hasCrsTransformEnabled():
              srsTransform = QgsCoordinateTransform(mapCanvasSrs, layerSrs)
              try:
                pos = srsTransform.transform(position)
              except QgsCsException, err:
                # ignore transformation errors
                continue

            if QGis.QGIS_VERSION_INT >= 10900: # for QGIS >= 1.9
              if not layer.dataProvider():
                continue

              ident = None
              if position is not None:
                canvas = self.iface.mapCanvas()

                # we can only use context if layer is not projected
                if canvas.hasCrsTransformEnabled() and layer.dataProvider().crs() != canvas.mapRenderer().destinationCrs():
                  ident = layer.dataProvider().identify(pos, QgsRasterDataProvider.IdentifyFormatValue )
                else:
                  extent = canvas.extent()
                  width = round(extent.width() / canvas.mapUnitsPerPixel());
                  height = round(extent.height() / canvas.mapUnitsPerPixel());

                  extent = canvas.mapRenderer().mapToLayerCoordinates( layer, extent );

                  ident = layer.dataProvider().identify(pos, QgsRasterDataProvider.IdentifyFormatValue, canvas.extent(), width, height )
                if not len( ident ) > 0:
                    continue

              # if given no position, set values to 0
              if position is None:
                  for key in ident.iterkeys():
                      ident[key] = layer.dataProvider().noDataValue(key)

              for iband in range(1,layer.bandCount()+1): # loop over the bands
                layernamewithband=layername
                if len(ident)>1:
                    layernamewithband+=' '+layer.bandName(iband)

                if not ident or not ident.has_key( iband ): # should not happen
                  bandvalue = "?"
                else: 
                  doubleValue =  ident[iband].toDouble()[0]
                  if layer.dataProvider().isNoDataValue ( iband, doubleValue ):  
                    bandvalue = "no data"
                  else:
                    bandvalue = QgsRasterBlock.printValue( doubleValue )

                self.values.append((layernamewithband,bandvalue))

                if needextremum:
                    has_stats=layer.hasStatistics(i)
                    if has_stats:
                        cstr=layer.bandStatistics(iband)
                    if has_stats:
                        self.ymin=min(self.ymin,cstr.minimumValue)
                        self.ymax=max(self.ymax,cstr.maximumValue)
                    else:
                        self.ymin=min(self.ymin,layer.minimumValue(i))
                        self.ymax=max(self.ymax,layer.maximumValue(i))

            else: # QGIS < 1.9
              isok,ident = layer.identify(pos)
              if not isok:
                  continue

              # if given no position, set values to 0
              if position is None:
                  for key in ident.iterkeys():
                      ident[key] = 0

              if layer.providerKey()=="grassraster":
                if not ident.has_key(QString("value")):
                  continue
                cstr = ident[QString("value")]
                if cstr.isNull():
                  continue
                value = cstr.toDouble()
                if not value[1]:
                  # if this is not a double, it is probably a (GRASS string like
                  # 'out of extent' or 'null (no data)'. Let's just show that:
                  self.values.append((layername, cstr))
                  continue
                self.values.append((layername,cstr))
                if needextremum:
                  self.ymin = min(self.ymin,value[0])
                  self.ymax = max(self.ymax,value[0])

              else:
                for iband in range(1,layer.bandCount()+1): # loop over the bands
                  bandvalue=ident[layer.bandName(iband)]
                  layernamewithband=layername
                  if len(ident)>1:
                      layernamewithband+=' '+layer.bandName(iband)

                  self.values.append((layernamewithband,bandvalue))

                  if needextremum:
                      if int(QGis.QGIS_VERSION[2]) > 8: # for QGIS > 1.8
                          has_stats=layer.dataProvider().hasStatistics(i)
                          if has_stats:
                              cstr=layer.dataProvider().bandStatistics(iband)
                      else:
                          has_stats=layer.hasStatistics(i)
                          if has_stats:
                              cstr=layer.bandStatistics(iband)
                      if has_stats:
                          self.ymin=min(self.ymin,cstr.minimumValue)
                          self.ymax=max(self.ymax,cstr.maximumValue)
                      else:
                          self.ymin=min(self.ymin,layer.minimumValue(i))
                          self.ymax=max(self.ymax,layer.maximumValue(i))

        self.showValues()
예제 #2
0
class MutantWidget(QWidget, Ui_Widget):
    def __init__(self, iface):
        self.hasqwt = has_qwt
        self.hasmpl = has_mpl
        self.haspqg = has_pyqtgraph
        self.layerMap = dict()
        self.statsChecked = False
        self.ymin = 0
        self.ymax = 365
        self.isActive = False
        self.mt_enabled = False

        # Statistics (>=1.9)
        self.statsSampleSize = 2500000
        self.stats = {}  # stats per layer

        self.layersSelected = []
        self.layerBands = dict()

        self.iface = iface
        self.canvas = self.iface.mapCanvas()

        self.legend = self.iface.legendInterface()
        self.logger = logging.getLogger('.'.join(
            (__name__, self.__class__.__name__)))

        QWidget.__init__(self)
        self.setupUi(self)
        self.tabWidget.setEnabled(False)
        self.plotOnMove.setChecked(QSettings().value(
            'plugins/mutant/mouseClick', False, type=bool))

        self.leYMin.setText(str(self.ymin))
        self.leYMax.setText(str(self.ymax))

        self.tracker = TimeTracker(self, self.canvas)
        self.filter = ApplyFilter(self, self.canvas)
        if has_mpl:
            self.mpl_cust = MplSettings(self, self.canvas)

        # self.setupUi_plot()
        # don't setup plot until Graph(1) tab is clicked - workaround for bug
        # #7450
        # qgis will still crash in some cases, but at least the tool can be
        # used in Table mode

        self.qwtPlot = None
        self.mplPlot = None
        self.mplLine = None

        QObject.connect(self.plotLibSelector,
                        SIGNAL("currentIndexChanged ( int )"),
                        self.change_plot)
        QObject.connect(self.tabWidget, SIGNAL("currentChanged ( int )"),
                        self.tabWidgetChanged)
        QObject.connect(self.layerSelection,
                        SIGNAL("currentIndexChanged ( int )"),
                        self.update_layers)
        QObject.connect(self.bandSelection,
                        SIGNAL("currentIndexChanged ( int )"),
                        self.update_layers)
        QObject.connect(self.selectionTable,
                        SIGNAL("cellChanged ( int , int )"),
                        self.layerSelected)
        QObject.connect(self.enableMTAnalysesCheckBox,
                        SIGNAL("toggled ( bool )"),
                        self.on_mt_analysis_toggled)
        QObject.connect(self.selectionStringLineEdit,
                        SIGNAL("textChanged ( QString )"), self.update_layers)
        QObject.connect(self.yAutoCheckBox, SIGNAL("toggled ( bool )"),
                        self.y_auto_toggle)

        QObject.connect(self.toggleMutant, SIGNAL("toggled ( bool )"),
                        self.catch_errors)

        self.exportPushButton.clicked.connect(self.export_values)
        # TODO Get Export from graph values
        # self.exportPushButton_2.clicked.connect(self.xxxx)

        self.registry = QgsMapLayerRegistry.instance()
        QObject.connect(self.registry,
                        SIGNAL("layersAdded(QList< QgsMapLayer * >)"),
                        self.catch_errors)
        QObject.connect(self.registry, SIGNAL("layersRemoved(QStringList)"),
                        self.catch_errors)
        self.setupUi_plot()

    def catch_errors(self):
        if self.toggleMutant.isChecked():
            layers = self.activeRasterLayers()
            if len(layers) == 0:
                if self.canvas.layerCount() > 0:
                    text = self.tr("Mutant: No valid layers to display - "
                                   "add Rasterlayers")
                    self.pop_messagebar(text)
                    self.changeActive(False)
                else:
                    text = self.tr("Mutant: No valid layers to display")
                    self.pop_messagebar(text)
                    self.changeActive(False)
                self.values = []
                return
            else:
                return
        else:
            return

    def y_auto_toggle(self, state):
        # User has toggled automatic (default) y min/max values
        if state == 1:
            self.leYMin.setEnabled(False)
            self.leYMax.setEnabled(False)
            self.leYMin.setText(str(self.ymin))
            self.leYMax.setText(str(self.ymax))
        else:
            self.leYMin.setEnabled(True)
            self.leYMax.setEnabled(True)

    def pop_messagebar(self, text, d_time=5):
        if d_time == 0:
            self.iface.messageBar().pushWidget(
                self.iface.messageBar().createMessage(text),
                QgsMessageBar.WARNING)
        else:
            self.iface.messageBar().pushWidget(
                self.iface.messageBar().createMessage(text),
                QgsMessageBar.WARNING, d_time)

    def setupUi_plot(self):
        # plot
        self.plotLibSelector.setVisible(False)
        self.enableStatistics.setVisible(False)
        # stats by default because estimated are fast
        self.enableStatistics.setChecked(True)

        plot_count = 0
        self.mplLine = None  # make sure to invalidate when layers change

        if self.hasqwt:  # Page 2 - qwt
            self.plotLibSelector.addItem('Qwt')
            plot_count += 1
            # Setup Qwt Plot Area in Widget
            self.qwtPlot = QwtPlot(self.stackedWidget)
            self.qwtPlot.setAutoFillBackground(False)
            self.qwtPlot.setObjectName("qwtPlot")
            self.curve = QwtPlotCurve()
            self.curve.setSymbol(
                QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.white), QPen(Qt.red, 2),
                          QSize(9, 9)))
            self.curve.attach(self.qwtPlot)

            # Size Policy ???
            sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
                                           QtGui.QSizePolicy.Expanding)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(
                self.qwtPlot.sizePolicy().hasHeightForWidth())
            self.qwtPlot.setSizePolicy(sizePolicy)
            # Size Policy ???

            self.qwtPlot.updateGeometry()
            self.stackedWidget.addWidget(self.qwtPlot)
            self.qwt_widgetnumber = self.stackedWidget.indexOf(self.qwtPlot)

        if self.hasmpl:  # Page 3 -  setup matplotlib
            self.plotLibSelector.addItem('matplotlib')
            # If matplotlib is the only one
            self.toggleInterpolation.setEnabled(True)
            self.toggleInterpolation.setVisible(True)
            plot_count += 1
            self.mplBackground = None
            # http://www.scipy.org/Cookbook/Matplotlib/Animations
            self.mplFig = plt.Figure(facecolor='w',
                                     edgecolor='g',
                                     linewidth=0.0)

            self.mpl_subplot = self.mplFig.add_subplot(111)
            self.pltCanvas = FigureCanvasQTAgg(self.mplFig)
            self.pltCanvas.setParent(self.stackedWidget)
            self.pltCanvas.setAutoFillBackground(False)
            self.pltCanvas.setObjectName("mplPlot")
            self.mplPlot = self.pltCanvas
            self.mplPlot.updateGeometry()
            self.stackedWidget.addWidget(self.mplPlot)
            self.mpl_widgetnumber = self.stackedWidget.indexOf(self.mplPlot)

        if self.haspqg:  # Page 3 - setup PyQtGraph
            self.plotLibSelector.addItem('PyQtGraph')
            plot_count += 1
            # Setup PyQtGraph stuff
            pg.setConfigOption('background', 'w')
            pg.setConfigOption('foreground', 'k')
            self.pqg_axis = DateTimeAxis(orientation='bottom')
            self.pqg_plot_widget = pg.PlotWidget(
                parent=self.stackedWidget, axisItems={'bottom': self.pqg_axis})
            self.pqg_plot_item = self.pqg_plot_widget.getPlotItem()
            self.pqg_plot_widget.updateGeometry()
            self.stackedWidget.addWidget(self.pqg_plot_widget)
            self.pqg_widgetnumber = self.stackedWidget.indexOf(
                self.pqg_plot_widget)
            # on zoom change do:
            self.pqg_plot_item.sigXRangeChanged.connect(self.refresh_ticks)

        if plot_count > 1:
            self.plotLibSelector.setEnabled(True)
            self.plotLibSelector.setVisible(True)
            self.plotLibSelector.setCurrentIndex(0)
            if self.hasqwt:
                self.plotLibSelector.setCurrentIndex(self.qwt_widgetnumber)
            elif self.hasmpl:
                self.plotLibSelector.setCurrentIndex(self.mpl_widgetnumber)
            else:
                self.plotLibSelector.setCurrentIndex(self.pqg_widgetnumber)
            self.change_plot()
        elif plot_count == 1:
            self.plotLibSelector.setCurrentIndex(0)
            self.change_plot()
        else:  # can only be 0 if nothing else matched
            message_text = "Mutant cannot find any graphiclibrary for " \
                           "creating plots. Please install either Qwt >= 5.0 " \
                           ",matplotlib >= 1.0 or PyQtGraph >= 0.9.8!"
            self.plot_message = QtGui.QLabel(message_text)
            self.plot_message.setWordWrap(True)
            self.stackedWidget.addWidget(self.plot_message)
            self.pop_messagebar(message_text)

    def change_plot(self):
        if self.stackedWidget.count() > 1:
            if self.plotLibSelector.currentText() == 'Qwt':
                self.stackedWidget.setCurrentIndex(self.qwt_widgetnumber)
                self.toggleInterpolation.setDisabled(True)
                self.toggleInterpolation.setVisible(False)
            elif self.plotLibSelector.currentText() == 'matplotlib':
                self.stackedWidget.setCurrentIndex(self.mpl_widgetnumber)
                self.toggleInterpolation.setEnabled(True)
                self.toggleInterpolation.setVisible(True)
                self.mpl_subplot.clear()
            elif self.plotLibSelector.currentText() == 'PyQtGraph':
                self.stackedWidget.setCurrentIndex(self.pqg_widgetnumber)
                self.toggleInterpolation.setDisabled(True)
                self.toggleInterpolation.setVisible(False)
                self.pqg_plot_widget.clear()
        elif self.stackedWidget.count() == 1:
            self.stackedWidget.setCurrentIndex(0)
        else:
            self.stackedWidget.setCurrentIndex(-1)

    def keyPressEvent(self, e):
        if (e.modifiers() == Qt.ControlModifier
                or e.modifiers() == Qt.MetaModifier) and e.key() == Qt.Key_C:
            items = ''
            for rec in range(self.valueTable.rowCount()):
                items += '"' + self.valueTable.item(
                    rec, 0).text() + '",' + self.valueTable.item(
                        rec, 1).text() + "\n"
            if not items == '':
                clipboard = QApplication.clipboard()
                clipboard.setText(items)
        else:
            QWidget.keyPressEvent(self, e)

    def changeActive(self, active, gui=True):
        self.isActive = active

        if active:
            self.toggleMutant.setCheckState(Qt.Checked)
            QObject.connect(self.canvas, SIGNAL("layersChanged ()"),
                            self.invalidatePlot)
            #QObject.connect(self.canvas,
            #                SIGNAL("layersChanged ()"),
            #                self.catch_errors)
            if not self.plotOnMove.isChecked():
                QObject.connect(self.canvas,
                                SIGNAL("xyCoordinates(const QgsPoint &)"),
                                self.printValue)
        else:
            self.toggleMutant.setCheckState(Qt.Unchecked)
            QObject.disconnect(self.canvas, SIGNAL("layersChanged ()"),
                               self.invalidatePlot)
            QObject.disconnect(self.canvas,
                               SIGNAL("xyCoordinates(const QgsPoint &)"),
                               self.printValue)

        if gui:
            self.tabWidget.setEnabled(active)
            if active:
                self.labelStatus.setText(self.tr("Mutant is enabled!"))
                # FIXME: Only on Options Tab?
                if self.tabWidget.currentIndex() == 2:
                    self.update_layers()
            else:
                self.labelStatus.setText(self.tr(""))
                #use this to clear plot when deactivated
                #self.values=[]
                #self.showValues()

    def activeRasterLayers(self, index=None):
        layers = []
        allLayers = []

        if not index:
            index = self.layerSelection.currentIndex()
        if index == 0:
            allLayers = self.canvas.layers()
        elif index == 1 or index == 3:
            allLayers = self.legend.layers()
        elif index == 2:
            for layer in self.legend.layers():
                if layer.id() in self.layersSelected:
                    allLayers.append(layer)

        for layer in allLayers:

            if index == 3:
                # Check if the layer name matches our filter and skip it if it
                # doesn't
                if not self.name_matches_filter(layer.name()):
                    continue

            if layer is not None and layer.isValid() and \
                    layer.type() == QgsMapLayer.RasterLayer and \
                    layer.dataProvider() and \
                    (layer.dataProvider().capabilities() & QgsRasterDataProvider.IdentifyValue):
                layers.append(layer)

        return layers

    def activeBandsForRaster(self, layer):
        activeBands = []

        if self.bandSelection.currentIndex() == 1 and layer.renderer():
            activeBands = layer.renderer().usesBands()
        elif self.bandSelection.currentIndex() == 2:
            if layer.bandCount() == 1:
                activeBands = [1]
            else:
                activeBands = self.layerBands[layer.id()] if (
                    layer.id() in self.layerBands) else []
        else:
            activeBands = range(1, layer.bandCount() + 1)

        return activeBands

    def printValue(self, position):

        if debug > 0:
            print(position)

        if not position:
            return
        if self.tabWidget.currentIndex() == 2:
            return

        if debug > 0:
            print("%d active rasters, %d canvas layers" %
                  (len(self.activeRasterLayers()), self.canvas.layerCount()))

        layers = self.activeRasterLayers()

        self.labelStatus.setText(
            self.tr('Coordinate:') + ' (%f, %f)' %
            (position.x(), position.y()))

        need_extremum = (self.tabWidget.currentIndex() == 1
                         )  # if plot is shown
        # count the number of required rows and remember the raster layers
        nrow = 0
        rasterlayers = []
        layersWOStatistics = []

        for layer in layers:
            nrow += layer.bandCount()
            rasterlayers.append(layer)

            # check statistics for each band
            if need_extremum:
                for i in range(1, layer.bandCount() + 1):
                    has_stats = self.get_statistics(layer, i) is not None
                    if not layer.id() in self.layerMap and not has_stats \
                            and not layer in layersWOStatistics:
                        layersWOStatistics.append(layer)

        if layersWOStatistics and not self.statsChecked:
            self.calculateStatistics(layersWOStatistics)

        irow = 0
        self.values = []
        self.ymin = 1e38
        self.ymax = -1e38

        mapCanvasSrs = self.iface.mapCanvas().mapRenderer().destinationCrs()

        # TODO - calculate the min/max values only once,
        # instead of every time!!!
        # And keep them in a dict() with key=layer.id()

        counter = 0
        for layer in rasterlayers:
            layer_name = unicode(layer.name())
            layer_srs = layer.crs()

            pos = position

            # if given no position, get dummy values
            if position is None:
                pos = QgsPoint(0, 0)
            # transform points if needed
            elif not mapCanvasSrs == layer_srs and \
                    self.iface.mapCanvas().hasCrsTransformEnabled():
                srsTransform = QgsCoordinateTransform(mapCanvasSrs, layer_srs)
                try:
                    pos = srsTransform.transform(position)
                except QgsCsException, err:
                    # ignore transformation errors
                    continue

            if True:
                if not layer.dataProvider():
                    continue
                ident = None

                if position is not None:
                    canvas = self.iface.mapCanvas()

                    # first test if point is within map layer extent
                    # maintain same behaviour as in 1.8 and print out of extent
                    if not layer.dataProvider().extent().contains(pos):
                        ident = dict()
                        for iband in range(1, layer.bandCount() + 1):
                            ident[iband] = str(self.tr('out of extent'))
                    # we can only use context if layer is not projected
                    elif canvas.hasCrsTransformEnabled() and \
                                    layer.dataProvider().crs() != \
                                    canvas.mapRenderer().destinationCrs():
                        ident = layer.dataProvider().identify(
                            pos, QgsRaster.IdentifyFormatValue).results()
                    else:
                        extent = canvas.extent()
                        width = round(extent.width() /
                                      canvas.mapUnitsPerPixel())
                        height = round(extent.height() /
                                       canvas.mapUnitsPerPixel())
                        extent = canvas.mapRenderer().mapToLayerCoordinates(
                            layer, extent)
                        ident = layer.dataProvider().identify(
                            pos, QgsRaster.IdentifyFormatValue,
                            canvas.extent(), width, height).results()
                    if not len(ident) > 0:
                        continue

                # if given no position, set values to 0
                if position is None and ident is not None and ident.iterkeys(
                ) is not None:
                    for key in ident.iterkeys():
                        ident[key] = layer.dataProvider().noDataValue(key)

                # bands displayed depends on cbxBands (all / active / selected)
                activeBands = self.activeBandsForRaster(layer)

                for iband in activeBands:  # loop over the active bands
                    layer_name_with_band = layer_name
                    if ident is not None and len(ident) > 1:
                        layer_name_with_band += ' ' + layer.bandName(iband)

                    if not ident or not ident.has_key(iband):
                        bandvalue = "?"
                    else:
                        bandvalue = ident[iband]
                        if bandvalue is None:
                            bandvalue = "no data"

                    # different x-Axis depending on if we want to use time or
                    # not
                    if self.mt_enabled:
                        layer_time = self.tracker.get_time_for_layer(layer)

                        if layer_time is None:
                            continue
                        else:
                            # pyqtgraph enabled convert date to epoch
                            graphlib = self.plotLibSelector.currentText()

                            if graphlib == 'PyQtGraph':
                                layer_time = time.mktime(
                                    layer_time.timetuple())
                                # overwrite
                            tup = (layer_name_with_band, layer_time,
                                   str(bandvalue))
                    else:
                        tup = (layer_name_with_band, counter + 1,
                               str(bandvalue))

                    self.values.append(tup)

                    if need_extremum:
                        # estimated statistics
                        stats = self.get_statistics(layer, iband)
                        if stats:
                            self.ymin = min(self.ymin, stats.minimumValue)
                            self.ymax = max(self.ymax, stats.maximumValue)
                    counter += 1

        # Update the ymin, ymax line edits if required
        if self.yAutoCheckBox.isChecked():
            self.leYMin.setText(str(self.ymin))
            self.leYMax.setText(str(self.ymax))

        self.values.sort(key=operator.itemgetter(1))

        if len(self.values) == 0:
            self.labelStatus.setText(self.tr("No valid bands to display"))

        self.showValues(position)
예제 #3
0
class ProfileDockWidget(QDockWidget):
    """
    DockWidget class to display the profile
    """

    closeSignal = pyqtSignal()

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QDockWidget.__init__(self)
        self.setWindowTitle(QCoreApplication.translate("VDLTools", "Profile Tool"))
        self.resize(1024, 400)
        self.__iface = iface
        self.__canvas = self.__iface.mapCanvas()
        self.__types = ['PDF', 'PNG']  # ], 'SVG', 'PS']
        self.__libs = []
        if Qwt5_loaded:
            self.__lib = 'Qwt5'
            self.__libs.append('Qwt5')
            if matplotlib_loaded:
                self.__libs.append('Matplotlib')
        elif matplotlib_loaded:
            self.__lib = 'Matplotlib'
            self.__libs.append('Matplotlib')
        else:
            self.__lib = None
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "No graph lib available (qwt5 or matplotlib)"),
                level=QgsMessageBar.CRITICAL, duration=0)

        self.__doTracking = False
        self.__vline = None

        self.__profiles = None
        self.__numLines = None
        self.__mntPoints = None

        self.__marker = None
        self.__tabmouseevent = None

        self.__contentWidget = QWidget()
        self.setWidget(self.__contentWidget)

        self.__boxLayout = QHBoxLayout()
        self.__contentWidget.setLayout(self.__boxLayout)

        self.__plotFrame = QFrame()
        self.__frameLayout = QHBoxLayout()
        self.__plotFrame.setLayout(self.__frameLayout)

        self.__printLayout = QHBoxLayout()
        self.__printLayout.addWidget(self.__plotFrame)

        self.__legendLayout = QVBoxLayout()
        self.__printLayout.addLayout(self.__legendLayout)

        self.__printWdg = QWidget()
        self.__printWdg.setLayout(self.__printLayout)

        self.__plotWdg = None
        self.__changePlotWidget()

        size = QSize(150, 20)

        self.__boxLayout.addWidget(self.__printWdg)

        self.__vertLayout = QVBoxLayout()

        self.__libCombo = QComboBox()
        self.__libCombo.setFixedSize(size)
        self.__libCombo.addItems(self.__libs)
        self.__vertLayout.addWidget(self.__libCombo)
        self.__libCombo.currentIndexChanged.connect(self.__setLib)

        self.__maxLabel = QLabel("y max")
        self.__maxLabel.setFixedSize(size)
        self.__vertLayout.addWidget(self.__maxLabel)
        self.__maxSpin = QSpinBox()
        self.__maxSpin.setFixedSize(size)
        self.__maxSpin.setRange(-10000, 10000)
        self.__maxSpin.valueChanged.connect(self.__reScalePlot)
        self.__vertLayout.addWidget(self.__maxSpin)
        self.__vertLayout.insertSpacing(10, 20)

        self.__minLabel = QLabel("y min")
        self.__minLabel.setFixedSize(size)
        self.__vertLayout.addWidget(self.__minLabel)
        self.__minSpin = QSpinBox()
        self.__minSpin.setFixedSize(size)
        self.__minSpin.setRange(-10000, 10000)
        self.__minSpin.valueChanged.connect(self.__reScalePlot)
        self.__vertLayout.addWidget(self.__minSpin)
        self.__vertLayout.insertSpacing(10, 40)

        self.__typeCombo = QComboBox()
        self.__typeCombo.setFixedSize(size)
        self.__typeCombo.addItems(self.__types)
        self.__vertLayout.addWidget(self.__typeCombo)
        self.__saveButton = QPushButton(QCoreApplication.translate("VDLTools", "Save"))
        self.__saveButton.setFixedSize(size)
        self.__saveButton.clicked.connect(self.__save)
        self.__vertLayout.addWidget(self.__saveButton)

        self.__boxLayout.addLayout(self.__vertLayout)

        self.__maxSpin.setEnabled(False)
        self.__minSpin.setEnabled(False)

        self.__colors = []
        for cn in QColor.colorNames():
            qc = QColor(cn)
            val = qc.red() + qc.green() + qc.blue()
            if 0 < val < 450:
                self.__colors.append(cn)

    def __changePlotWidget(self):
        """
        When plot widget is change (qwt <-> matplotlib)
        """
        self.__activateMouseTracking(False)
        while self.__frameLayout.count():
            child = self.__frameLayout.takeAt(0)
            child.widget().deleteLater()
        self.__plotWdg = None

        if self.__lib == 'Qwt5':
            self.__plotWdg = QwtPlot(self.__plotFrame)
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            sizePolicy.setHorizontalStretch(10)
            sizePolicy.setVerticalStretch(0)
            self.__plotWdg.setSizePolicy(sizePolicy)
            self.__plotWdg.setAutoFillBackground(False)
            # Decoration
            self.__plotWdg.setCanvasBackground(Qt.white)
            self.__plotWdg.plotLayout().setAlignCanvasToScales(False)
            self.__plotWdg.plotLayout().setSpacing(100)
            self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.xBottom)
            self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.yLeft)
            title = QwtText(QCoreApplication.translate("VDLTools", "Distance [m]"))
            title.setFont(QFont("Helvetica", 10))
            self.__plotWdg.setAxisTitle(QwtPlot.xBottom, title)
            title.setText(QCoreApplication.translate("VDLTools", "Elevation [m]"))
            title.setFont(QFont("Helvetica", 10))
            self.__plotWdg.setAxisTitle(QwtPlot.yLeft, title)
            self.__zoomer = QwtPlotZoomer(QwtPlot.xBottom, QwtPlot.yLeft, QwtPicker.DragSelection, QwtPicker.AlwaysOff,
                                          self.__plotWdg.canvas())
            self.__zoomer.setRubberBandPen(QPen(Qt.blue))
            grid = QwtPlotGrid()
            grid.setPen(QPen(QColor('grey'), 0, Qt.DotLine))
            grid.attach(self.__plotWdg)
            self.__frameLayout.addWidget(self.__plotWdg)

        elif self.__lib == 'Matplotlib':
            # __plotWdg.figure : matplotlib.figure.Figure
            fig = Figure((1.0, 1.0), linewidth=0.0, subplotpars=SubplotParams(left=0, bottom=0, right=1, top=1,
                                                                              wspace=0, hspace=0))

            font = {'family': 'arial', 'weight': 'normal', 'size': 12}
            rc('font', **font)

            rect = fig.patch
            rect.set_facecolor((0.9, 0.9, 0.9))

            self.__axes = fig.add_axes((0.07, 0.16, 0.92, 0.82))
            self.__axes.set_xbound(0, 1000)
            self.__axes.set_ybound(0, 1000)
            self.__manageMatplotlibAxe(self.__axes)
            self.__plotWdg = FigureCanvasQTAgg(fig)
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            self.__plotWdg.setSizePolicy(sizePolicy)
            self.__frameLayout.addWidget(self.__plotWdg)

    def setProfiles(self, profiles, numLines):
        """
        To set the profiles
        :param profiles: profiles : positions with elevations (for line and points)
        :param numLines: number of selected connected lines
        """
        self.__numLines = numLines
        self.__profiles = profiles
        if self.__lib == 'Matplotlib':
            self.__prepare_points()

    def __getLinearPoints(self):
        """
        To extract the linear points of the profile
        """
        profileLen = 0
        self.__profiles[0]['l'] = profileLen
        for i in range(0, len(self.__profiles)-1):
            x1 = float(self.__profiles[i]['x'])
            y1 = float(self.__profiles[i]['y'])
            x2 = float(self.__profiles[i+1]['x'])
            y2 = float(self.__profiles[i+1]['y'])
            profileLen += sqrt(((x2-x1)*(x2-x1)) + ((y2-y1)*(y2-y1)))
            self.__profiles[i+1]['l'] = profileLen

    def __getMnt(self, settings):
        """
        To get the MN data for the profile
        :param settings: settings containing MN url
        """
        if settings is None or settings.mntUrl is None or settings.mntUrl == "None":
            url = 'http://map.lausanne.ch/main/wsgi/profile.json'
        elif settings.mntUrl == "":
            return
        else:
            url = settings.mntUrl
        names = ['mnt', 'mns', 'toit_rocher']
        url += '?layers='
        pos = 0
        for name in names:
            if pos > 0:
                url += ','
            pos += 1
            url += name
        url += '&geom={"type":"LineString","coordinates":['
        pos = 0
        for i in range(len(self.__profiles)):
            if pos > 0:
                url += ','
            pos += 1
            url += '[' + str(self.__profiles[i]['x']) + ',' + str(self.__profiles[i]['y']) + ']'
        url = url + ']}&nbPoints=' + str(int(self.__profiles[len(self.__profiles)-1]['l']))
        try:
            response = urlopen(url)
            j = response.read()
            j_obj = json.loads(j)
            profile = j_obj['profile']
            self.__mntPoints = []
            self.__mntPoints.append(names)
            mnt_l = []
            mnt_z = []
            for p in range(len(names)):
                z = []
                mnt_z.append(z)
            for pt in profile:
                mnt_l.append(float(pt['dist']))
                values = pt['values']
                for p in range(len(names)):
                    if names[p] in values:
                        mnt_z[p].append(float(values[names[p]]))
                    else:
                        mnt_z[p].append(None)
            self.__mntPoints.append(mnt_l)
            self.__mntPoints.append(mnt_z)
        except HTTPError as e:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "HTTP Error"),
                QCoreApplication.translate("VDLTools", "status error [" + str(e.code) + "] : " + e.reason),
                level=QgsMessageBar.CRITICAL, duration=0)
        except URLError as e:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "URL Error"),
                e.reason, level=QgsMessageBar.CRITICAL, duration=0)

    def attachCurves(self, names, settings, usedMnts):
        """
        To attach the curves for the layers to the profile
        :param names: layers names
        """
        if (self.__profiles is None) or (self.__profiles == 0):
            return

        self.__getLinearPoints()
        if usedMnts is not None and (usedMnts[0] or usedMnts[1] or usedMnts[2]):
            self.__getMnt(settings)

        c = 0

        if self.__mntPoints is not None:
                for p in range(len(self.__mntPoints[0])):
                    if usedMnts[p]:
                        legend = QLabel("<font color='" + self.__colors[c] + "'>" + self.__mntPoints[0][p]
                                        + "</font>")
                        self.__legendLayout.addWidget(legend)

                        if self.__lib == 'Qwt5':

                            xx = [list(g) for k, g in itertools.groupby(self.__mntPoints[1],
                                                                        lambda x: x is None) if not k]
                            yy = [list(g) for k, g in itertools.groupby(self.__mntPoints[2][p], lambda x: x is None)
                                  if not k]

                            for j in range(len(xx)):
                                curve = QwtPlotCurve(self.__mntPoints[0][p])
                                curve.setData(xx[j], yy[j])
                                curve.setPen(QPen(QColor(self.__colors[c]), 3))
                                curve.attach(self.__plotWdg)

                        elif self.__lib == 'Matplotlib':
                            qcol = QColor(self.__colors[c])
                            self.__plotWdg.figure.get_axes()[0].plot(self.__mntPoints[1], self.__mntPoints[2][p],
                                                                     gid=self.__mntPoints[0][p], linewidth=3)
                            tmp = self.__plotWdg.figure.get_axes()[0].get_lines()
                            for t in range(len(tmp)):
                                if self.__mntPoints[0][p] == tmp[t].get_gid():
                                    tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0),
                                                      old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0)))
                                    self.__plotWdg.draw()
                                    break
                        c += 1

        if 'z' in self.__profiles[0]:
            for i in range(len(self.__profiles[0]['z'])):
                if i < self.__numLines:
                    v = 0
                else:
                    v = i - self.__numLines + 1
                name = names[v]
                xx = []
                yy = []
                for prof in self.__profiles:
                    xx.append(prof['l'])
                    yy.append(prof['z'][i])

                for j in range(len(yy)):
                    if yy[j] is None:
                        xx[j] = None

                if i == 0 or i > (self.__numLines-1):
                    legend = QLabel("<font color='" + self.__colors[c] + "'>" + name + "</font>")
                    self.__legendLayout.addWidget(legend)

                if self.__lib == 'Qwt5':

                    # Split xx and yy into single lines at None values
                    xx = [list(g) for k, g in itertools.groupby(xx, lambda x: x is None) if not k]
                    yy = [list(g) for k, g in itertools.groupby(yy, lambda x: x is None) if not k]

                    # Create & attach one QwtPlotCurve per one single line
                    for j in range(len(xx)):
                        curve = QwtPlotCurve(name)
                        curve.setData(xx[j], yy[j])
                        curve.setPen(QPen(QColor(self.__colors[c]), 3))
                        if i > (self.__numLines-1):
                            curve.setStyle(QwtPlotCurve.Dots)
                            pen = QPen(QColor(self.__colors[c]), 8)
                            pen.setCapStyle(Qt.RoundCap)
                            curve.setPen(pen)
                        curve.attach(self.__plotWdg)

                elif self.__lib == 'Matplotlib':
                    qcol = QColor(self.__colors[c])
                    if i < self.__numLines:
                        self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=3)
                    else:
                        self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=5, marker='o',
                                                                 linestyle='None')
                    tmp = self.__plotWdg.figure.get_axes()[0].get_lines()
                    for t in range(len(tmp)):
                        if name == tmp[t].get_gid():
                            tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0),
                                              old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0)))
                            self.__plotWdg.draw()
                            break
                c += 1

        # scaling this
        try:
            self.__reScalePlot(None, True)
        except:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Rescale problem... (trace printed)"),
                level=QgsMessageBar.CRITICAL, duration=0)
            print(
                QCoreApplication.translate("VDLTools", "rescale problem : "), sys.exc_info()[0], traceback.format_exc())
        if self.__lib == 'Qwt5':
            self.__plotWdg.replot()
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].redraw_in_frame()
            self.__plotWdg.draw()
            self.__activateMouseTracking(True)
            self.__marker.show()

    def __reScalePlot(self, value=None, auto=False):
        """
        To rescale the profile plot depending to the bounds
        """
        if (self.__profiles is None) or (self.__profiles == 0):
            self.__plotWdg.replot()
            return

        maxi = 0
        for i in range(len(self.__profiles)):
            if (int(self.__profiles[i]['l'])) > maxi:
                maxi = int(self.__profiles[i]['l']) + 1
        if self.__lib == 'Qwt5':
            self.__plotWdg.setAxisScale(2, 0, maxi, 0)
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].set_xbound(0, maxi)

        minimumValue = self.__minSpin.value()
        maximumValue = self.__maxSpin.value()

        # to set max y and min y displayed
        if auto:
            minimumValue = 1000000000
            maximumValue = -1000000000
            for i in range(len(self.__profiles)):
                if 'z' in self.__profiles[i]:
                    mini = self.__minTab(self.__profiles[i]['z'])
                    if int(mini) < minimumValue:
                        minimumValue = int(mini) - 1
                    maxi = self.__maxTab(self.__profiles[i]['z'])
                    if int(maxi) > maximumValue:
                        maximumValue = int(maxi) + 1
                if self.__mntPoints is not None:
                    for pts in self.__mntPoints[2]:
                        miniMnt = self.__minTab(pts)
                        if int(miniMnt) < minimumValue:
                            minimumValue = int(miniMnt) - 1
                        maxiMnt = self.__maxTab(pts)
                        if int(maxiMnt) > maximumValue:
                            maximumValue = int(maxiMnt) + 1
        self.__maxSpin.setValue(maximumValue)
        self.__minSpin.setValue(minimumValue)
        self.__maxSpin.setEnabled(True)
        self.__minSpin.setEnabled(True)

        if self.__lib == 'Qwt5':
            rect = QRectF(0, minimumValue, maxi, maximumValue-minimumValue)
            self.__zoomer.setZoomBase(rect)

        # to draw vertical lines
        for i in range(len(self.__profiles)):
            zz = []
            for j in range(self.__numLines):
                if self.__profiles[i]['z'][j] is not None:
                    zz.append(j)
            color = None
            if len(zz) == 2:
                width = 3
                color = QColor('red')
            else:
                width = 1

            if self.__lib == 'Qwt5':
                vertLine = QwtPlotMarker()
                vertLine.setLineStyle(QwtPlotMarker.VLine)
                pen = vertLine.linePen()
                pen.setWidth(width)
                if color is not None:
                    pen.setColor(color)
                vertLine.setLinePen(pen)
                vertLine.setXValue(self.__profiles[i]['l'])
                label = vertLine.label()
                label.setText(str(i))
                vertLine.setLabel(label)
                vertLine.setLabelAlignment(Qt.AlignLeft)
                vertLine.attach(self.__plotWdg)
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.get_axes()[0].vlines(self.__profiles[i]['l'], minimumValue, maximumValue,
                                                           linewidth=width)

        if minimumValue < maximumValue:
            if self.__lib == 'Qwt5':
                self.__plotWdg.setAxisScale(0, minimumValue, maximumValue, 0)
                self.__plotWdg.replot()
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.get_axes()[0].set_ybound(minimumValue, maximumValue)
                self.__plotWdg.figure.get_axes()[0].redraw_in_frame()
                self.__plotWdg.draw()

    @staticmethod
    def __minTab(tab):
        """
        To get the minimum value in a table
        :param tab: table to scan
        :return: minimum value
        """
        mini = 1000000000
        for t in tab:
            if t is None:
                continue
            if t < mini:
                mini = t
        return mini

    @staticmethod
    def __maxTab(tab):
        """
        To get the maximum value in a table
        :param tab: table to scan
        :return: maximum value
        """
        maxi = -1000000000
        for t in tab:
            if t is None:
                continue
            if t > maxi:
                maxi = t
        return maxi

    def __setLib(self):
        """
        To set the new widget library (qwt <-> matplotlib)
        """
        self.__lib = self.__libs[self.__libCombo.currentIndex()]
        self.__changePlotWidget()

    def __save(self):
        """
        To save the profile in a file, on selected format
        """
        idx = self.__typeCombo.currentIndex()
        if idx == 0:
            self.__outPDF()
        elif idx == 1:
            self.__outPNG()
        else:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Invalid index ") + str(idx),
                level=QgsMessageBar.CRITICAL, duration=0)

    def __outPDF(self):
        """
        To save the profile as pdf file
        """
        fileName = QFileDialog.getSaveFileName(
            self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"),
            QCoreApplication.translate("VDLTools", "Profile.pdf"),"Portable Document Format (*.pdf)")
        if fileName is not None:
            if self.__lib == 'Qwt5':
                printer = QPrinter()
                printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin"))
                printer.setOutputFileName(fileName)
                printer.setOutputFormat(QPrinter.PdfFormat)
                printer.setOrientation(QPrinter.Landscape)
                self.__plotWdg.print_(printer)
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.savefig(str(fileName))
            # printer = QPrinter()
            # printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin"))
            # printer.setOutputFileName(fileName)
            # printer.setOutputFormat(QPrinter.PdfFormat)
            # printer.setOrientation(QPrinter.Landscape)
            # printer.setPaperSize(QSizeF(self.__printWdg.size()), QPrinter.Millimeter)
            # printer.setFullPage(True)
            # self.__printWdg.render(printer)

    def __outPNG(self):
        """
        To save the profile as png file
        """
        fileName = QFileDialog.getSaveFileName(
            self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"),
            QCoreApplication.translate("VDLTools", "Profile.png"),"Portable Network Graphics (*.png)")
        if fileName is not None:
            QPixmap.grabWidget(self.__printWdg).save(fileName, "PNG")

    def clearData(self):
        """
        To clear the displayed data
        """
        if self.__profiles is None:
            return
        if self.__lib == 'Qwt5':
            self.__plotWdg.clear()
            self.__profiles = None
            temp1 = self.__plotWdg.itemList()
            for j in range(len(temp1)):
                if temp1[j].rtti() == QwtPlotItem.Rtti_PlotCurve:
                    temp1[j].detach()
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].cla()
            self.__manageMatplotlibAxe(self.__plotWdg.figure.get_axes()[0])
        self.__maxSpin.setEnabled(False)
        self.__minSpin.setEnabled(False)
        self.__maxSpin.setValue(0)
        self.__minSpin.setValue(0)

        # clear legend
        while self.__legendLayout.count():
            child = self.__legendLayout.takeAt(0)
            child.widget().deleteLater()

    def __manageMatplotlibAxe(self, axe):
        """
        To manage the axes for matplotlib library
        :param axe: the axes element
        """
        axe.grid()
        axe.tick_params(axis="both", which="major", direction="out", length=10, width=1, bottom=True, top=False,
                        left=True, right=False)
        axe.minorticks_on()
        axe.tick_params(axis="both", which="minor", direction="out", length=5, width=1, bottom=True, top=False,
                        left=True, right=False)
        axe.set_xlabel(QCoreApplication.translate("VDLTools", "Distance [m]"))
        axe.set_ylabel(QCoreApplication.translate("VDLTools", "Elevation [m]"))

    def __activateMouseTracking(self, activate):
        """
        To (de)activate the mouse tracking on the profile for matplotlib library
        :param activate: true to activate, false to deactivate
        """
        if activate:
            self.__doTracking = True
            self.__loadRubber()
            self.cid = self.__plotWdg.mpl_connect('motion_notify_event', self.__mouseevent_mpl)
        elif self.__doTracking:
            self.__doTracking = False
            self.__plotWdg.mpl_disconnect(self.cid)
            if self.__marker is not None:
                self.__canvas.scene().removeItem(self.__marker)
            try:
                if self.__vline is not None:
                    self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline)
                    self.__plotWdg.draw()
            except Exception as e:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "Tracking exception : ") + str(e),
                    level=QgsMessageBar.CRITICAL, duration=0)

    def __mouseevent_mpl(self, event):
        """
        To manage matplotlib mouse tracking event
        :param event: mouse tracking event
        """
        if event.xdata is not None:
            try:
                if self.__vline is not None:
                    self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline)
            except Exception as e:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "Mouse event exception : ") + str(e),
                    level=QgsMessageBar.CRITICAL, duration=0)
            xdata = float(event.xdata)
            self.__vline = self.__plotWdg.figure.get_axes()[0].axvline(xdata, linewidth=2, color='k')
            self.__plotWdg.draw()
            i = 1
            while i < len(self.__tabmouseevent)-1 and xdata > self.__tabmouseevent[i][0]:
                i += 1
            i -= 1

            x = self.__tabmouseevent[i][1] + (self.__tabmouseevent[i + 1][1] - self.__tabmouseevent[i][1]) / (
            self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0])
            y = self.__tabmouseevent[i][2] + (self.__tabmouseevent[i + 1][2] - self.__tabmouseevent[i][2]) / (
            self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0])
            self.__marker.show()
            self.__marker.setCenter(QgsPoint(x, y))

    def __loadRubber(self):
        """
        To load te rubber band for mouse tracking on map
        """
        self.__marker = QgsVertexMarker(self.__canvas)
        self.__marker.setIconSize(5)
        self.__marker.setIconType(QgsVertexMarker.ICON_BOX)
        self.__marker.setPenWidth(3)

    def __prepare_points(self):
        """
        To prepare the points on map for mouse tracking on profile
        """
        self.__tabmouseevent = []
        length = 0
        for i, point in enumerate(self.__profiles):
            if i == 0:
                self.__tabmouseevent.append([0, point['x'], point['y']])
            else:
                length += ((self.__profiles[i]['x'] - self.__profiles[i-1]['x']) ** 2 +
                           (self.__profiles[i]['y'] - self.__profiles[i-1]['y']) ** 2) ** 0.5
                self.__tabmouseevent.append([float(length), float(point['x']), float(point['y'])])

    def closeEvent(self, event):
        """
        When the dock widget is closed
        :param event: close event
        """
        if self.__maxSpin is not None:
            Signal.safelyDisconnect(self.__maxSpin.valueChanged, self.__reScalePlot)
            self.__maxSpin = None
        if self.__minSpin is not None:
            Signal.safelyDisconnect(self.__minSpin.valueChanged, self.__reScalePlot)
            self.__minSpin = None
        if self.__saveButton is not None:
            Signal.safelyDisconnect(self.__saveButton.clicked, self.__save)
            self.__saveButton = None
        if self.__libCombo is not None:
            Signal.safelyDisconnect(self.__libCombo.currentIndexChanged, self.__setLib)
            self.__libCombo = None
        self.closeSignal.emit()
        if self.__marker is not None:
            self.__marker.hide()
        QDockWidget.closeEvent(self, event)
예제 #4
0
class ValueWidget(QWidget, Ui_Widget):

    def __init__(self, iface):

        self.hasqwt=hasqwt
        self.hasmpl=hasmpl
        self.layerMap=dict()
        self.statsChecked=False
        self.ymin=0
        self.ymax=250
        self.isActive=False

        # Statistics (>=1.9)
        self.statsSampleSize = 2500000
        self.stats = {} # stats per layer

        self.layersSelected=[]
        self.layerBands=dict()

        self.iface=iface
        self.canvas=self.iface.mapCanvas
        self.legend=self.iface.legendInterface()
        self.logger = logging.getLogger('.'.join((__name__, 
                                        self.__class__.__name__)))

        QWidget.__init__(self)
        self.setupUi(self)
        self.tabWidget.setEnabled(False)
        self.cbxClick.setChecked( QSettings().value('plugins/valuetool/mouseClick', False, type=bool ) )

        #self.setupUi_plot()
        #don't setup plot until Plot tab is clicked - workaround for bug #7450
        #qgis will still crash in some cases, but at least the tool can be used in Table mode
        self.qwtPlot = None
        self.mplPlot = None
        self.mplLine = None

        QObject.connect(self.plotSelector, SIGNAL( "currentIndexChanged ( int )" ), self.changePlot )
        QObject.connect(self.tabWidget, SIGNAL( "currentChanged ( int )" ), self.tabWidgetChanged )
        QObject.connect(self.cbxLayers, SIGNAL( "currentIndexChanged ( int )" ), self.updateLayers )
        QObject.connect(self.cbxBands, SIGNAL( "currentIndexChanged ( int )" ), self.updateLayers )
        QObject.connect(self.tableWidget2, SIGNAL("cellChanged ( int , int )"), self.layerSelected)

    def setupUi_plot(self):

        # plot
        self.plotSelector.setVisible( False )
        self.cbxStats.setVisible( False )
        # stats by default because estimated are fast
        self.cbxStats.setChecked( True )
        self.plotSelector.addItem( 'Qwt' )
        self.plotSelector.addItem( 'mpl' )

        # Page 2 - qwt
        if self.hasqwt:
            self.qwtPlot = QwtPlot(self.stackedWidget)
            self.qwtPlot.setAutoFillBackground(False)
            self.qwtPlot.setObjectName("qwtPlot")
            self.curve = QwtPlotCurve()
            self.curve.setSymbol(
                QwtSymbol(QwtSymbol.Ellipse,
                          QBrush(Qt.white),
                          QPen(Qt.red, 2),
                          QSize(9, 9)))
            self.curve.attach(self.qwtPlot)
        else:
            self.qwtPlot = QtGui.QLabel("Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.qwtPlot.sizePolicy().hasHeightForWidth())
        self.qwtPlot.setSizePolicy(sizePolicy)
        self.qwtPlot.updateGeometry()
        self.stackedWidget.addWidget(self.qwtPlot)

        #Page 3 - matplotlib
        self.mplLine = None #make sure to invalidate when layers change
        if self.hasmpl:
            # mpl stuff
            # should make figure light gray
            self.mplBackground = None #http://www.scipy.org/Cookbook/Matplotlib/Animations
            self.mplFig = plt.Figure(facecolor='w', edgecolor='w')
            self.mplFig.subplots_adjust(left=0.1, right=0.975, bottom=0.13, top=0.95)
            self.mplPlt = self.mplFig.add_subplot(111)   
            self.mplPlt.tick_params(axis='both', which='major', labelsize=12)
            self.mplPlt.tick_params(axis='both', which='minor', labelsize=10)                           
            # qt stuff
            self.pltCanvas = FigureCanvasQTAgg(self.mplFig)
            self.pltCanvas.setParent(self.stackedWidget)
            self.pltCanvas.setAutoFillBackground(False)
            self.pltCanvas.setObjectName("mplPlot")
            self.mplPlot = self.pltCanvas
        else:
            self.mplPlot = QtGui.QLabel("Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.mplPlot.sizePolicy().hasHeightForWidth())
        self.mplPlot.setSizePolicy(sizePolicy)
        self.mplPlot.updateGeometry()
        self.stackedWidget.addWidget(self.mplPlot)

        if (self.hasqwt and self.hasmpl):
            self.plotSelector.setEnabled(True)
            self.plotSelector.setVisible(True)
            self.plotSelector.setCurrentIndex(0);
        else:
            if self.hasqwt:
                self.plotSelector.setCurrentIndex(0);
            else:
                self.plotSelector.setCurrentIndex(1);
        self.changePlot()

    def keyPressEvent( self, e ):
      if ( e.modifiers() == Qt.ControlModifier or e.modifiers() == Qt.MetaModifier ) and e.key() == Qt.Key_C:
        items = ''
        for rec in range( self.tableWidget.rowCount() ):
          items += '"' + self.tableWidget.item( rec, 0 ).text() + '",' + self.tableWidget.item( rec, 1 ).text() + "\n"
        if not items == '':
          clipboard = QApplication.clipboard()
          clipboard.setText( items )
      else:
        QWidget.keyPressEvent( self, e )

    def changePlot(self):
        if (self.plotSelector.currentText()=='mpl'):
            self.stackedWidget.setCurrentIndex(1)
        else:
            self.stackedWidget.setCurrentIndex(0)

    def changeActive(self,active,gui=True):
        self.isActive=active
        
        if (active):
            self.cbxEnable.setCheckState(Qt.Checked)
            QObject.connect(self.canvas, SIGNAL( "layersChanged ()" ), self.invalidatePlot )
            if not self.cbxClick.isChecked():
                QObject.connect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint &)"), self.printValue)
        else:
            self.cbxEnable.setCheckState(Qt.Unchecked)
            QObject.disconnect(self.canvas, SIGNAL( "layersChanged ()" ), self.invalidatePlot )
            QObject.disconnect(self.canvas, SIGNAL("xyCoordinates(const QgsPoint &)"), self.printValue)

        if gui:
            self.tabWidget.setEnabled(active)
            if active:
                self.labelStatus.setText(self.tr("Value tool is enabled"))
                if self.tabWidget.currentIndex()==2:
                    self.updateLayers()
            else:
                self.labelStatus.setText(self.tr(""))
                #use this to clear plot when deactivated
                #self.values=[]
                #self.showValues()

    def activeRasterLayers(self, index=None):
        layers=[]
        allLayers=[]

        if not index: 
            index=self.cbxLayers.currentIndex()
        if index == 0:
            allLayers=self.canvas.layers()
        elif index == 1:
            allLayers=self.legend.getLayers()
        elif index == 2:
            for layer in self.legend.getLayers():
                if layer.id() in self.layersSelected:
                    allLayers.append(layer)
        
        for layer in allLayers:
            if layer!=None and layer.isValid() and \
                    layer.type()==QgsMapLayer.RasterLayer and \
                    layer.dataProvider() and \
                    (layer.dataProvider().capabilities() & QgsRasterDataProvider.IdentifyValue):
                  layers.append(layer)

        return layers

    def activeBandsForRaster(self,layer):
        activeBands=[]

        if self.cbxBands.currentIndex() == 1 and layer.renderer():
            activeBands = layer.renderer().usesBands()                 
        elif self.cbxBands.currentIndex() == 2:
            if layer.bandCount()==1:
                activeBands=[1]
            else:
                activeBands = self.layerBands[layer.id()] if (layer.id() in self.layerBands) else []
        else:
            activeBands = range(1,layer.bandCount()+1)
        
        return activeBands

    def printValue(self,position):

        if debug > 0:
            print(position)

        if not position:
            return
        if self.tabWidget.currentIndex()==2:
            return

        if debug > 0:
            print("%d active rasters, %d canvas layers" %(len(self.activeRasterLayers()),self.canvas.layerCount()))
        layers = self.activeRasterLayers()
        if len(layers) == 0:
            if self.canvas.layerCount() > 0:
                self.labelStatus.setText(self.tr("No valid layers to display - change layers in options"))
            else:
                self.labelStatus.setText(self.tr("No valid layers to display"))
            self.values=[]         
            self.showValues()
            return
        
        self.labelStatus.setText(self.tr('Coordinate:') + ' (%f, %f)' % (position.x(), position.y()))

        needextremum = (self.tabWidget.currentIndex()==1) # if plot is shown

        # count the number of requires rows and remember the raster layers
        nrow=0
        rasterlayers=[]
        layersWOStatistics=[]

        for layer in layers:

            nrow+=layer.bandCount()
            rasterlayers.append(layer)

            # check statistics for each band
            if needextremum:
                for i in range( 1,layer.bandCount()+1 ):
                    has_stats = self.getStats ( layer, i ) is not None
                    if not layer.id() in self.layerMap and not has_stats\
                            and not layer in layersWOStatistics:
                        layersWOStatistics.append(layer)

        if layersWOStatistics and not self.statsChecked:
          self.calculateStatistics(layersWOStatistics)
                  
        irow=0
        self.values=[]
        self.ymin=1e38
        self.ymax=-1e38

        mapCanvasSrs = self.iface.mapCanvas.mapRenderer().destinationCrs()

        # TODO - calculate the min/max values only once, instead of every time!!!
        # keep them in a dict() with key=layer.id()
        
        
        #pull out wavelength if it exists in metadata
        #piece to pull out wavelength information if present in metadata
        rasterMeta=rasterlayers[0].metadata()
        self.wavelengths={}
        self.wavelength_units=''
        if('wavelength' in rasterMeta):
            mdSplit=rasterMeta.split('</p>')
            for d in mdSplit:
                if ('Band_' in d and 'glossy' not in d and '=' in d):
                    variableName,valueWavelength=d.split('=')
                    bandNumber=int(variableName.split('_')[1])
                    self.wavelengths[bandNumber]=float(valueWavelength.split(' ')[-2].replace('(',''))
                elif('wavelength_units' in d):
                    variableName,v=d.split('=')
                    self.wavelength_units=v    
         ####         
        for layer in rasterlayers:
            
            layername=unicode(layer.name())
            layerSrs = layer.crs()

            pos = position         

            # if given no position, get dummy values
            if position is None:
                pos = QgsPoint(0,0)
            # transform points if needed
            elif not mapCanvasSrs == layerSrs and self.iface.mapCanvas.hasCrsTransformEnabled():
              srsTransform = QgsCoordinateTransform(mapCanvasSrs, layerSrs)
              try:
                pos = srsTransform.transform(position)
              except QgsCsException, err:
                # ignore transformation errors
                continue

            if True: # for QGIS >= 1.9
              if not layer.dataProvider():
                continue

              ident = None
              if position is not None:
                canvas = self.iface.mapCanvas
                # first test if point is within map layer extent 
                # maintain same behaviour as in 1.8 and print out of extent
                if not layer.dataProvider().extent().contains( pos ):
                  ident = dict()
                  for iband in range(1,layer.bandCount()+1):
                    ident[iband] = QVariant(self.tr('out of extent'))
                # we can only use context if layer is not projected
                elif canvas.hasCrsTransformEnabled() and layer.dataProvider().crs() != canvas.mapRenderer().destinationCrs():
                  ident = layer.dataProvider().identify(pos, QgsRaster.IdentifyFormatValue ).results()
                else:
                  extent = canvas.extent()
                  width = round(extent.width() / canvas.mapUnitsPerPixel());
                  height = round(extent.height() / canvas.mapUnitsPerPixel());

                  extent = canvas.mapRenderer().mapToLayerCoordinates( layer, extent );

                  ident = layer.dataProvider().identify(pos, QgsRaster.IdentifyFormatValue, canvas.extent(), width, height ).results()
                if not len( ident ) > 0:
                    continue

              # if given no position, set values to 0
              if position is None and ident is not None and ident.iterkeys() is not None:
                  for key in ident.iterkeys():
                      ident[key] = layer.dataProvider().noDataValue(key)

              # bands displayed depends on cbxBands (all / active / selected)
              activeBands = self.activeBandsForRaster(layer) 
                  
              for iband in activeBands: # loop over the active bands
                layernamewithband=layername
                if ident is not None and len(ident)>1:
                    layernamewithband+=' '+layer.bandName(iband)

                if not ident or not ident.has_key( iband ): # should not happen
                  bandvalue = "?"
                else:
                  bandvalue = ident[iband].toString()
                  if bandvalue is None:
                      bandvalue = "no data"
             
                self.values.append((layernamewithband,str(bandvalue)))

                if needextremum:
                  # estimated statistics
                  stats = self.getStats ( layer, iband )
                  if stats:
                    self.ymin=min(self.ymin,stats.minimumValue)
                    self.ymax=max(self.ymax,stats.maximumValue)

        if len(self.values) == 0:
            self.labelStatus.setText(self.tr("No valid bands to display"))

        self.showValues()
예제 #5
0
class ValueWidget(QWidget, Ui_Widget):
    def __init__(self, iface):

        self.hasqwt = hasqwt
        self.hasmpl = hasmpl
        self.layerMap = dict()
        self.statsChecked = False
        self.ymin = 0
        self.ymax = 250
        self.isActive = False

        # Statistics (>=1.9)
        self.statsSampleSize = 2500000
        self.stats = {}  # stats per layer

        self.layersSelected = []
        self.layerBands = dict()

        self.iface = iface
        self.canvas = self.iface.mapCanvas
        self.legend = self.iface.legendInterface()
        self.logger = logging.getLogger('.'.join(
            (__name__, self.__class__.__name__)))

        QWidget.__init__(self)
        self.setupUi(self)
        self.tabWidget.setEnabled(False)
        self.cbxClick.setChecked(QSettings().value(
            'plugins/valuetool/mouseClick', False, type=bool))

        #self.setupUi_plot()
        #don't setup plot until Plot tab is clicked - workaround for bug #7450
        #qgis will still crash in some cases, but at least the tool can be used in Table mode
        self.qwtPlot = None
        self.mplPlot = None
        self.mplLine = None

        QObject.connect(self.plotSelector,
                        SIGNAL("currentIndexChanged ( int )"), self.changePlot)
        QObject.connect(self.tabWidget, SIGNAL("currentChanged ( int )"),
                        self.tabWidgetChanged)
        QObject.connect(self.cbxLayers, SIGNAL("currentIndexChanged ( int )"),
                        self.updateLayers)
        QObject.connect(self.cbxBands, SIGNAL("currentIndexChanged ( int )"),
                        self.updateLayers)
        QObject.connect(self.tableWidget2, SIGNAL("cellChanged ( int , int )"),
                        self.layerSelected)

    def setupUi_plot(self):

        # plot
        self.plotSelector.setVisible(False)
        self.cbxStats.setVisible(False)
        # stats by default because estimated are fast
        self.cbxStats.setChecked(True)
        self.plotSelector.addItem('Qwt')
        self.plotSelector.addItem('mpl')

        # Page 2 - qwt
        if self.hasqwt:
            self.qwtPlot = QwtPlot(self.stackedWidget)
            self.qwtPlot.setAutoFillBackground(False)
            self.qwtPlot.setObjectName("qwtPlot")
            self.curve = QwtPlotCurve()
            self.curve.setSymbol(
                QwtSymbol(QwtSymbol.Ellipse, QBrush(Qt.white), QPen(Qt.red, 2),
                          QSize(9, 9)))
            self.curve.attach(self.qwtPlot)
        else:
            self.qwtPlot = QtGui.QLabel(
                "Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.qwtPlot.sizePolicy().hasHeightForWidth())
        self.qwtPlot.setSizePolicy(sizePolicy)
        self.qwtPlot.updateGeometry()
        self.stackedWidget.addWidget(self.qwtPlot)

        #Page 3 - matplotlib
        self.mplLine = None  #make sure to invalidate when layers change
        if self.hasmpl:
            # mpl stuff
            # should make figure light gray
            self.mplBackground = None  #http://www.scipy.org/Cookbook/Matplotlib/Animations
            self.mplFig = plt.Figure(facecolor='w', edgecolor='w')
            self.mplFig.subplots_adjust(left=0.1,
                                        right=0.975,
                                        bottom=0.13,
                                        top=0.95)
            self.mplPlt = self.mplFig.add_subplot(111)
            self.mplPlt.tick_params(axis='both', which='major', labelsize=12)
            self.mplPlt.tick_params(axis='both', which='minor', labelsize=10)
            # qt stuff
            self.pltCanvas = FigureCanvasQTAgg(self.mplFig)
            self.pltCanvas.setParent(self.stackedWidget)
            self.pltCanvas.setAutoFillBackground(False)
            self.pltCanvas.setObjectName("mplPlot")
            self.mplPlot = self.pltCanvas
        else:
            self.mplPlot = QtGui.QLabel(
                "Need Qwt >= 5.0 or matplotlib >= 1.0 !")

        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
                                       QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.mplPlot.sizePolicy().hasHeightForWidth())
        self.mplPlot.setSizePolicy(sizePolicy)
        self.mplPlot.updateGeometry()
        self.stackedWidget.addWidget(self.mplPlot)

        if (self.hasqwt and self.hasmpl):
            self.plotSelector.setEnabled(True)
            self.plotSelector.setVisible(True)
            self.plotSelector.setCurrentIndex(0)
        else:
            if self.hasqwt:
                self.plotSelector.setCurrentIndex(0)
            else:
                self.plotSelector.setCurrentIndex(1)
        self.changePlot()

    def keyPressEvent(self, e):
        if (e.modifiers() == Qt.ControlModifier
                or e.modifiers() == Qt.MetaModifier) and e.key() == Qt.Key_C:
            items = ''
            for rec in range(self.tableWidget.rowCount()):
                items += '"' + self.tableWidget.item(
                    rec, 0).text() + '",' + self.tableWidget.item(
                        rec, 1).text() + "\n"
            if not items == '':
                clipboard = QApplication.clipboard()
                clipboard.setText(items)
        else:
            QWidget.keyPressEvent(self, e)

    def changePlot(self):
        if (self.plotSelector.currentText() == 'mpl'):
            self.stackedWidget.setCurrentIndex(1)
        else:
            self.stackedWidget.setCurrentIndex(0)

    def changeActive(self, active, gui=True):
        self.isActive = active

        if (active):
            self.cbxEnable.setCheckState(Qt.Checked)
            QObject.connect(self.canvas, SIGNAL("layersChanged ()"),
                            self.invalidatePlot)
            if not self.cbxClick.isChecked():
                QObject.connect(self.canvas,
                                SIGNAL("xyCoordinates(const QgsPoint &)"),
                                self.printValue)
        else:
            self.cbxEnable.setCheckState(Qt.Unchecked)
            QObject.disconnect(self.canvas, SIGNAL("layersChanged ()"),
                               self.invalidatePlot)
            QObject.disconnect(self.canvas,
                               SIGNAL("xyCoordinates(const QgsPoint &)"),
                               self.printValue)

        if gui:
            self.tabWidget.setEnabled(active)
            if active:
                self.labelStatus.setText(self.tr("Value tool is enabled"))
                if self.tabWidget.currentIndex() == 2:
                    self.updateLayers()
            else:
                self.labelStatus.setText(self.tr(""))
                #use this to clear plot when deactivated
                #self.values=[]
                #self.showValues()

    def activeRasterLayers(self, index=None):
        layers = []
        allLayers = []

        if not index:
            index = self.cbxLayers.currentIndex()
        if index == 0:
            allLayers = self.canvas.layers()
        elif index == 1:
            allLayers = self.legend.getLayers()
        elif index == 2:
            for layer in self.legend.getLayers():
                if layer.id() in self.layersSelected:
                    allLayers.append(layer)

        for layer in allLayers:
            if layer!=None and layer.isValid() and \
                    layer.type()==QgsMapLayer.RasterLayer and \
                    layer.dataProvider() and \
                    (layer.dataProvider().capabilities() & QgsRasterDataProvider.IdentifyValue):
                layers.append(layer)

        return layers

    def activeBandsForRaster(self, layer):
        activeBands = []

        if self.cbxBands.currentIndex() == 1 and layer.renderer():
            activeBands = layer.renderer().usesBands()
        elif self.cbxBands.currentIndex() == 2:
            if layer.bandCount() == 1:
                activeBands = [1]
            else:
                activeBands = self.layerBands[layer.id()] if (
                    layer.id() in self.layerBands) else []
        else:
            activeBands = range(1, layer.bandCount() + 1)

        return activeBands

    def printValue(self, position):

        if debug > 0:
            print(position)

        if not position:
            return
        if self.tabWidget.currentIndex() == 2:
            return

        if debug > 0:
            print("%d active rasters, %d canvas layers" %
                  (len(self.activeRasterLayers()), self.canvas.layerCount()))
        layers = self.activeRasterLayers()
        if len(layers) == 0:
            if self.canvas.layerCount() > 0:
                self.labelStatus.setText(
                    self.tr(
                        "No valid layers to display - change layers in options"
                    ))
            else:
                self.labelStatus.setText(self.tr("No valid layers to display"))
            self.values = []
            self.showValues()
            return

        self.labelStatus.setText(
            self.tr('Coordinate:') + ' (%f, %f)' %
            (position.x(), position.y()))

        needextremum = (self.tabWidget.currentIndex() == 1)  # if plot is shown

        # count the number of requires rows and remember the raster layers
        nrow = 0
        rasterlayers = []
        layersWOStatistics = []

        for layer in layers:

            nrow += layer.bandCount()
            rasterlayers.append(layer)

            # check statistics for each band
            if needextremum:
                for i in range(1, layer.bandCount() + 1):
                    has_stats = self.getStats(layer, i) is not None
                    if not layer.id() in self.layerMap and not has_stats\
                            and not layer in layersWOStatistics:
                        layersWOStatistics.append(layer)

        if layersWOStatistics and not self.statsChecked:
            self.calculateStatistics(layersWOStatistics)

        irow = 0
        self.values = []
        self.ymin = 1e38
        self.ymax = -1e38

        mapCanvasSrs = self.iface.mapCanvas.mapRenderer().destinationCrs()

        # TODO - calculate the min/max values only once, instead of every time!!!
        # keep them in a dict() with key=layer.id()

        #pull out wavelength if it exists in metadata
        #piece to pull out wavelength information if present in metadata
        rasterMeta = rasterlayers[0].metadata()
        self.wavelengths = {}
        self.wavelength_units = ''
        if ('wavelength' in rasterMeta):
            mdSplit = rasterMeta.split('</p>')
            for d in mdSplit:
                if ('Band_' in d and 'glossy' not in d and '=' in d):
                    variableName, valueWavelength = d.split('=')
                    bandNumber = int(variableName.split('_')[1])
                    self.wavelengths[bandNumber] = float(
                        valueWavelength.split(' ')[-2].replace('(', ''))
                elif ('wavelength_units' in d):
                    variableName, v = d.split('=')
                    self.wavelength_units = v
        ####
        for layer in rasterlayers:

            layername = unicode(layer.name())
            layerSrs = layer.crs()

            pos = position

            # if given no position, get dummy values
            if position is None:
                pos = QgsPoint(0, 0)
            # transform points if needed
            elif not mapCanvasSrs == layerSrs and self.iface.mapCanvas.hasCrsTransformEnabled(
            ):
                srsTransform = QgsCoordinateTransform(mapCanvasSrs, layerSrs)
                try:
                    pos = srsTransform.transform(position)
                except QgsCsException, err:
                    # ignore transformation errors
                    continue

            if True:  # for QGIS >= 1.9
                if not layer.dataProvider():
                    continue

                ident = None
                if position is not None:
                    canvas = self.iface.mapCanvas
                    # first test if point is within map layer extent
                    # maintain same behaviour as in 1.8 and print out of extent
                    if not layer.dataProvider().extent().contains(pos):
                        ident = dict()
                        for iband in range(1, layer.bandCount() + 1):
                            ident[iband] = QVariant(self.tr('out of extent'))
                    # we can only use context if layer is not projected
                    elif canvas.hasCrsTransformEnabled(
                    ) and layer.dataProvider().crs() != canvas.mapRenderer(
                    ).destinationCrs():
                        ident = layer.dataProvider().identify(
                            pos, QgsRaster.IdentifyFormatValue).results()
                    else:
                        extent = canvas.extent()
                        width = round(extent.width() /
                                      canvas.mapUnitsPerPixel())
                        height = round(extent.height() /
                                       canvas.mapUnitsPerPixel())

                        extent = canvas.mapRenderer().mapToLayerCoordinates(
                            layer, extent)

                        ident = layer.dataProvider().identify(
                            pos, QgsRaster.IdentifyFormatValue,
                            canvas.extent(), width, height).results()
                    if not len(ident) > 0:
                        continue

                # if given no position, set values to 0
                if position is None and ident is not None and ident.iterkeys(
                ) is not None:
                    for key in ident.iterkeys():
                        ident[key] = layer.dataProvider().noDataValue(key)

                # bands displayed depends on cbxBands (all / active / selected)
                activeBands = self.activeBandsForRaster(layer)

                for iband in activeBands:  # loop over the active bands
                    layernamewithband = layername
                    if ident is not None and len(ident) > 1:
                        layernamewithband += ' ' + layer.bandName(iband)

                    if not ident or not ident.has_key(
                            iband):  # should not happen
                        bandvalue = "?"
                    else:
                        bandvalue = ident[iband].toString()
                        if bandvalue is None:
                            bandvalue = "no data"

                    self.values.append((layernamewithband, str(bandvalue)))

                    if needextremum:
                        # estimated statistics
                        stats = self.getStats(layer, iband)
                        if stats:
                            self.ymin = min(self.ymin, stats.minimumValue)
                            self.ymax = max(self.ymax, stats.maximumValue)

        if len(self.values) == 0:
            self.labelStatus.setText(self.tr("No valid bands to display"))

        self.showValues()
예제 #6
0
class ProfileDockWidget(QDockWidget):
    """
    DockWidget class to display the profile
    """

    closeSignal = pyqtSignal()

    def __init__(self, iface, geometry, mntButton=False, zerosButton=False):
        """
        Constructor
        :param iface: interface
        :param width: dock widget geometry
        """
        QDockWidget.__init__(self)
        self.setWindowTitle(QCoreApplication.translate("VDLTools", "Profile Tool"))
        self.__iface = iface
        self.__geom = geometry
        self.__canvas = self.__iface.mapCanvas()
        self.__types = ['PDF', 'PNG']  # ], 'SVG', 'PS']
        self.__libs = []
        if Qwt5_loaded:
            self.__lib = 'Qwt5'
            self.__libs.append('Qwt5')
            if matplotlib_loaded:
                self.__libs.append('Matplotlib')
        elif matplotlib_loaded:
            self.__lib = 'Matplotlib'
            self.__libs.append('Matplotlib')
        else:
            self.__lib = None
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "No graph lib available (qwt5 or matplotlib)"),
                level=QgsMessageBar.CRITICAL, duration=0)

        self.__doTracking = False
        self.__vline = None

        self.__profiles = None
        self.__numLines = None
        self.__mntPoints = None

        self.__marker = None
        self.__tabmouseevent = None

        if self.__geom is not None:
            self.setGeometry(self.__geom)

        self.__contentWidget = QWidget()
        self.setWidget(self.__contentWidget)

        self.__boxLayout = QHBoxLayout()
        self.__contentWidget.setLayout(self.__boxLayout)

        self.__plotFrame = QFrame()
        self.__frameLayout = QHBoxLayout()
        self.__plotFrame.setLayout(self.__frameLayout)

        self.__printLayout = QHBoxLayout()
        self.__printLayout.addWidget(self.__plotFrame)

        self.__legendLayout = QVBoxLayout()
        self.__printLayout.addLayout(self.__legendLayout)

        self.__printWdg = QWidget()
        self.__printWdg.setLayout(self.__printLayout)

        self.__plotWdg = None
        self.__changePlotWidget()

        size = QSize(150, 20)

        self.__boxLayout.addWidget(self.__printWdg)

        self.__vertLayout = QVBoxLayout()

        self.__libCombo = QComboBox()
        self.__libCombo.setFixedSize(size)
        self.__libCombo.addItems(self.__libs)
        self.__vertLayout.addWidget(self.__libCombo)
        self.__libCombo.currentIndexChanged.connect(self.__setLib)

        if mntButton:
            self.__displayMnt = False
            self.__mntButton = QPushButton(QCoreApplication.translate("VDLTools", "Display MNT"))
            self.__mntButton.setFixedSize(size)
            self.__mntButton.clicked.connect(self.__mnt)
            self.__vertLayout.addWidget(self.__mntButton)

        if zerosButton:
            self.__displayZeros = False
            self.__zerosButton = QPushButton(QCoreApplication.translate("VDLTools", "Display Zeros"))
            self.__zerosButton.setFixedSize(size)
            self.__zerosButton.clicked.connect(self.__zeros)
            self.__vertLayout.addWidget(self.__zerosButton)
        else:
            self.__displayZeros = True

        self.__maxLabel = QLabel("y max")
        self.__maxLabel.setFixedSize(size)
        self.__vertLayout.addWidget(self.__maxLabel)
        self.__maxSpin = QSpinBox()
        self.__maxSpin.setFixedSize(size)
        self.__maxSpin.setRange(-10000, 10000)
        self.__maxSpin.valueChanged.connect(self.__reScalePlot)
        self.__vertLayout.addWidget(self.__maxSpin)
        self.__vertLayout.insertSpacing(10, 20)

        self.__minLabel = QLabel("y min")
        self.__minLabel.setFixedSize(size)
        self.__vertLayout.addWidget(self.__minLabel)
        self.__minSpin = QSpinBox()
        self.__minSpin.setFixedSize(size)
        self.__minSpin.setRange(-10000, 10000)
        self.__minSpin.valueChanged.connect(self.__reScalePlot)
        self.__vertLayout.addWidget(self.__minSpin)
        self.__vertLayout.insertSpacing(10, 40)

        self.__typeCombo = QComboBox()
        self.__typeCombo.setFixedSize(size)
        self.__typeCombo.addItems(self.__types)
        self.__vertLayout.addWidget(self.__typeCombo)
        self.__saveButton = QPushButton(QCoreApplication.translate("VDLTools", "Save"))
        self.__saveButton.setFixedSize(size)
        self.__saveButton.clicked.connect(self.__save)
        self.__vertLayout.addWidget(self.__saveButton)

        self.__boxLayout.addLayout(self.__vertLayout)

        self.__maxSpin.setEnabled(False)
        self.__minSpin.setEnabled(False)

        self.__colors = []
        for cn in QColor.colorNames():
            qc = QColor(cn)
            val = qc.red() + qc.green() + qc.blue()
            if 0 < val < 450:
                self.__colors.append(cn)

    def mntButton(self):
        """
        To get the mnt button instance
        :return: mnt button instance
        """
        return self.__mntButton

    def zerosButton(self):
        """
        To get the zeros button instance
        :return: zeros button instance
        """
        return self.__zerosButton

    def displayMnt(self):
        """
        To get if we want to display mnt
        :return: true or false
        """
        return self.__displayMnt

    def __mnt(self):
        """
        To toggle mnt display choice
        """
        if self.__displayMnt:
            self.__displayMnt = False
            self.__mntButton.setText(QCoreApplication.translate("VDLTools", "Display MNT"))
        else:
            self.__displayMnt = True
            self.__mntButton.setText(QCoreApplication.translate("VDLTools", "Remove MNT"))

    def __zeros(self):
        """
        To toggle if we want to display zero elevations or not
        """
        if self.__displayZeros:
            self.__displayZeros = False
            self.__zerosButton.setText(QCoreApplication.translate("VDLTools", "Display Zeros"))
        else:
            self.__displayZeros = True
            self.__zerosButton.setText(QCoreApplication.translate("VDLTools", "Remove Zeros"))

    def __changePlotWidget(self):
        """
        When plot widget is change (qwt <-> matplotlib)
        """
        self.__activateMouseTracking(False)
        while self.__frameLayout.count():
            child = self.__frameLayout.takeAt(0)
            child.widget().deleteLater()
        self.__plotWdg = None

        if self.__lib == 'Qwt5':
            self.__plotWdg = QwtPlot(self.__plotFrame)
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            sizePolicy.setHorizontalStretch(10)
            sizePolicy.setVerticalStretch(0)
            self.__plotWdg.setSizePolicy(sizePolicy)
            self.__plotWdg.setAutoFillBackground(False)
            # Decoration
            self.__plotWdg.setCanvasBackground(Qt.white)
            self.__plotWdg.plotLayout().setAlignCanvasToScales(False)
            self.__plotWdg.plotLayout().setSpacing(100)
            self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.xBottom)
            self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.yLeft)
            title = QwtText(QCoreApplication.translate("VDLTools", "Distance [m]"))
            title.setFont(QFont("Helvetica", 10))
            self.__plotWdg.setAxisTitle(QwtPlot.xBottom, title)
            title.setText(QCoreApplication.translate("VDLTools", "Elevation [m]"))
            title.setFont(QFont("Helvetica", 10))
            self.__plotWdg.setAxisTitle(QwtPlot.yLeft, title)
            self.__zoomer = QwtPlotZoomer(QwtPlot.xBottom, QwtPlot.yLeft, QwtPicker.DragSelection, QwtPicker.AlwaysOff,
                                          self.__plotWdg.canvas())
            self.__zoomer.setRubberBandPen(QPen(Qt.blue))
            grid = QwtPlotGrid()
            grid.setPen(QPen(QColor('grey'), 0, Qt.DotLine))
            grid.attach(self.__plotWdg)
            self.__frameLayout.addWidget(self.__plotWdg)

        elif self.__lib == 'Matplotlib':
            fig = Figure((1.0, 1.0), linewidth=0.0, subplotpars=SubplotParams(left=0, bottom=0, right=1, top=1,
                                                                              wspace=0, hspace=0))

            font = {'family': 'arial', 'weight': 'normal', 'size': 12}
            rc('font', **font)

            rect = fig.patch
            rect.set_facecolor((0.9, 0.9, 0.9))

            self.__axes = fig.add_axes((0.07, 0.16, 0.92, 0.82))
            self.__axes.set_xbound(0, 1000)
            self.__axes.set_ybound(0, 1000)
            self.__manageMatplotlibAxe(self.__axes)
            self.__plotWdg = FigureCanvasQTAgg(fig)
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            self.__plotWdg.setSizePolicy(sizePolicy)
            self.__frameLayout.addWidget(self.__plotWdg)

    def setProfiles(self, profiles, numLines):
        """
        To set the profiles
        :param profiles: profiles : positions with elevations (for line and points)
        :param numLines: number of selected connected lines
        """
        self.__numLines = numLines
        self.__profiles = profiles
        if self.__lib == 'Matplotlib':
            self.__prepare_points()

    def __getLinearPoints(self):
        """
        To extract the linear points of the profile
        """
        profileLen = 0
        self.__profiles[0]['l'] = profileLen
        for i in range(0, len(self.__profiles)-1):
            x1 = float(self.__profiles[i]['x'])
            y1 = float(self.__profiles[i]['y'])
            x2 = float(self.__profiles[i+1]['x'])
            y2 = float(self.__profiles[i+1]['y'])
            profileLen += sqrt(((x2-x1)*(x2-x1)) + ((y2-y1)*(y2-y1)))
            self.__profiles[i+1]['l'] = profileLen

    def __getMnt(self, settings):
        """
        To get the MN data for the profile
        :param settings: settings containing MN url
        """
        if settings is None or settings.mntUrl is None or settings.mntUrl == "None":
            url = 'https://map.lausanne.ch/prod/wsgi/profile.json'
        elif settings.mntUrl == "":
            return
        else:
            url = settings.mntUrl
        names = ['MNT', 'MNS', 'Rocher (approx.)']
        data = "layers=MNT%2CMNS%2CRocher%20(approx.)&geom=%7B%22type%22%3A%22LineString%22%2C%22coordinates%22%3A%5B"

        pos = 0
        for i in range(len(self.__profiles)):
            if pos > 0:
                data += "%2C"
            pos += 1
            data += "%5B" + str(self.__profiles[i]['x']) + "%2C" + str(self.__profiles[i]['y']) + "%5D"
        data += "%5D%7D&nbPoints=" + str(int(self.__profiles[len(self.__profiles)-1]['l']+1))
        try:
            response = requests.post(url, data=data)
            j = response.text
            j_obj = json.loads(j)
            profile = j_obj['profile']
            self.__mntPoints = []
            self.__mntPoints.append(names)
            mnt_l = []
            mnt_z = []
            for p in range(len(names)):
                z = []
                mnt_z.append(z)
            for pt in profile:
                mnt_l.append(float(pt['dist']))
                values = pt['values']
                for p in range(len(names)):
                    if names[p] in values:
                        mnt_z[p].append(float(values[names[p]]))
                    else:
                        mnt_z[p].append(None)
            self.__mntPoints.append(mnt_l)
            self.__mntPoints.append(mnt_z)
        except HTTPError as e:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "HTTP Error"),
                QCoreApplication.translate("VDLTools", "status error") + "[" + str(e.code) + "] : " + e.reason,
                level=QgsMessageBar.CRITICAL, duration=0)
        except URLError as e:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "URL Error"),
                e.reason, level=QgsMessageBar.CRITICAL, duration=0)
        except ValueError as e:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "No MNT values here"),
                level=QgsMessageBar.CRITICAL, duration=0)


    def attachCurves(self, names, settings, usedMnts):
        """
        To attach the curves for the layers to the profile
        :param names: layers names
        """
        if (self.__profiles is None) or (self.__profiles == 0):
            return

        self.__getLinearPoints()
        if usedMnts is not None and (usedMnts[0] or usedMnts[1] or usedMnts[2]):
            self.__getMnt(settings)

        c = 0

        if self.__mntPoints is not None:
                for p in range(len(self.__mntPoints[0])):
                    if usedMnts[p]:
                        legend = QLabel("<font color='" + self.__colors[c] + "'>" + self.__mntPoints[0][p]
                                        + "</font>")
                        self.__legendLayout.addWidget(legend)

                        if self.__lib == 'Qwt5':

                            xx = [list(g) for k, g in itertools.groupby(self.__mntPoints[1],
                                                                        lambda x: x is None) if not k]
                            yy = [list(g) for k, g in itertools.groupby(self.__mntPoints[2][p], lambda x: x is None)
                                  if not k]

                            for j in range(len(xx)):
                                curve = QwtPlotCurve(self.__mntPoints[0][p])
                                curve.setData(xx[j], yy[j])
                                curve.setPen(QPen(QColor(self.__colors[c]), 3))
                                curve.attach(self.__plotWdg)

                        elif self.__lib == 'Matplotlib':
                            qcol = QColor(self.__colors[c])
                            self.__plotWdg.figure.get_axes()[0].plot(self.__mntPoints[1], self.__mntPoints[2][p],
                                                                     gid=self.__mntPoints[0][p], linewidth=3)
                            tmp = self.__plotWdg.figure.get_axes()[0].get_lines()
                            for t in range(len(tmp)):
                                if self.__mntPoints[0][p] == tmp[t].get_gid():
                                    tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0),
                                                      old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0)))
                                    self.__plotWdg.draw()
                                    break
                        c += 1

        if 'z' in self.__profiles[0]:
            for i in range(len(self.__profiles[0]['z'])):
                if i < self.__numLines:
                    v = 0
                else:
                    v = i - self.__numLines + 1
                name = names[v]
                xx = []
                yy = []
                for prof in self.__profiles:
                    xx.append(prof['l'])
                    yy.append(prof['z'][i])

                for j in range(len(yy)):
                    if yy[j] is None:
                        xx[j] = None

                if i == 0 or i > (self.__numLines-1):
                    legend = QLabel("<font color='" + self.__colors[c] + "'>" + name + "</font>")
                    self.__legendLayout.addWidget(legend)

                if self.__lib == 'Qwt5':

                    # Split xx and yy into single lines at None values
                    xx = [list(g) for k, g in itertools.groupby(xx, lambda x: x is None) if not k]
                    yy = [list(g) for k, g in itertools.groupby(yy, lambda x: x is None) if not k]

                    # Create & attach one QwtPlotCurve per one single line
                    for j in range(len(xx)):
                        curve = QwtPlotCurve(name)
                        curve.setData(xx[j], yy[j])
                        curve.setPen(QPen(QColor(self.__colors[c]), 3))
                        if i > (self.__numLines-1):
                            curve.setStyle(QwtPlotCurve.Dots)
                            pen = QPen(QColor(self.__colors[c]), 8)
                            pen.setCapStyle(Qt.RoundCap)
                            curve.setPen(pen)
                        curve.attach(self.__plotWdg)

                elif self.__lib == 'Matplotlib':
                    qcol = QColor(self.__colors[c])
                    if i < self.__numLines:
                        self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=3)
                    else:
                        self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=5, marker='o',
                                                                 linestyle='None')
                    tmp = self.__plotWdg.figure.get_axes()[0].get_lines()
                    for t in range(len(tmp)):
                        if name == tmp[t].get_gid():
                            tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0),
                                              old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0)))
                            self.__plotWdg.draw()
                            break
                c += 1

        # scaling this
        try:
            self.__reScalePlot(None, True)
        except:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Rescale problem... (trace printed)"),
                level=QgsMessageBar.CRITICAL, duration=0)
            print(sys.exc_info()[0], traceback.format_exc())
        if self.__lib == 'Qwt5':
            self.__plotWdg.replot()
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].redraw_in_frame()
            self.__plotWdg.draw()
            self.__activateMouseTracking(True)
            self.__marker.show()

    def __reScalePlot(self, value=None, auto=False):
        """
        To rescale the profile plot depending to the bounds
        :param value: juste because connections give value
        :param auto: if automatic ranges calcul is wanted
        """
        if (self.__profiles is None) or (self.__profiles == 0):
            self.__plotWdg.replot()
            return

        maxi = 0
        for i in range(len(self.__profiles)):
            if (ceil(self.__profiles[i]['l'])) > maxi:
                maxi = ceil(self.__profiles[i]['l'])
        if self.__lib == 'Qwt5':
            self.__plotWdg.setAxisScale(2, 0, maxi, 0)
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].set_xbound(0, maxi)

        minimumValue = self.__minSpin.value()
        maximumValue = self.__maxSpin.value()

        # to set max y and min y displayed
        if auto:
            minimumValue = 1000000000
            maximumValue = -1000000000
            for i in range(len(self.__profiles)):
                if 'z' in self.__profiles[i]:
                    mini = self.__minTab(self.__profiles[i]['z'])
                    if (mini > 0 or self.__displayZeros) and mini < minimumValue:
                        minimumValue = ceil(mini) - 1
                    maxi = self.__maxTab(self.__profiles[i]['z'])
                    if maxi > maximumValue:
                        maximumValue = floor(maxi) + 1
                if self.__mntPoints is not None:
                    for pts in self.__mntPoints[2]:
                        miniMnt = self.__minTab(pts)
                        if (miniMnt > 0 or self.__displayZeros) and miniMnt < minimumValue:
                            minimumValue = ceil(miniMnt) - 1
                        maxiMnt = self.__maxTab(pts)
                        if maxiMnt > maximumValue:
                            maximumValue = floor(maxiMnt) + 1
        self.__maxSpin.setValue(maximumValue)
        self.__minSpin.setValue(minimumValue)
        self.__maxSpin.setEnabled(True)
        self.__minSpin.setEnabled(True)

        if self.__lib == 'Qwt5':
            rect = QRectF(0, minimumValue, maxi, maximumValue-minimumValue)
            self.__zoomer.setZoomBase(rect)

        # to draw vertical lines
        for i in range(len(self.__profiles)):
            zz = []
            for j in range(self.__numLines):
                if self.__profiles[i]['z'][j] is not None:
                    zz.append(j)
            color = None
            if len(zz) == 2:
                width = 3
                color = QColor('red')
            else:
                width = 1

            if self.__lib == 'Qwt5':
                vertLine = QwtPlotMarker()
                vertLine.setLineStyle(QwtPlotMarker.VLine)
                pen = vertLine.linePen()
                pen.setWidth(width)
                if color is not None:
                    pen.setColor(color)
                vertLine.setLinePen(pen)
                vertLine.setXValue(self.__profiles[i]['l'])
                label = vertLine.label()
                label.setText(str(i))
                vertLine.setLabel(label)
                vertLine.setLabelAlignment(Qt.AlignLeft)
                vertLine.attach(self.__plotWdg)
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.get_axes()[0].vlines(self.__profiles[i]['l'], minimumValue, maximumValue,
                                                           linewidth=width)

        if minimumValue < maximumValue:
            if self.__lib == 'Qwt5':
                self.__plotWdg.setAxisScale(0, minimumValue, maximumValue, 0)
                self.__plotWdg.replot()
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.get_axes()[0].set_ybound(minimumValue, maximumValue)
                self.__plotWdg.figure.get_axes()[0].redraw_in_frame()
                self.__plotWdg.draw()

    @staticmethod
    def __minTab(tab):
        """
        To get the minimum value in a table
        :param tab: table to scan
        :return: minimum value
        """
        mini = 1000000000
        for t in tab:
            if t is None:
                continue
            if t < mini:
                mini = t
        return mini

    @staticmethod
    def __maxTab(tab):
        """
        To get the maximum value in a table
        :param tab: table to scan
        :return: maximum value
        """
        maxi = -1000000000
        for t in tab:
            if t is None:
                continue
            if t > maxi:
                maxi = t
        return maxi

    def __setLib(self):
        """
        To set the new widget library (qwt <-> matplotlib)
        """
        self.__lib = self.__libs[self.__libCombo.currentIndex()]
        self.__changePlotWidget()

    def __save(self):
        """
        To save the profile in a file, on selected format
        """
        idx = self.__typeCombo.currentIndex()
        if idx == 0:
            self.__outPDF()
        elif idx == 1:
            self.__outPNG()
        else:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Invalid index ") + str(idx),
                level=QgsMessageBar.CRITICAL, duration=0)

    def __outPDF(self):
        """
        To save the profile as pdf file
        """
        fileName = QFileDialog.getSaveFileName(
            self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"),
            QCoreApplication.translate("VDLTools", "Profile.pdf"),"Portable Document Format (*.pdf)")
        if fileName is not None:
            if self.__lib == 'Qwt5':
                printer = QPrinter()
                printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin"))
                printer.setOutputFileName(fileName)
                printer.setOutputFormat(QPrinter.PdfFormat)
                printer.setOrientation(QPrinter.Landscape)
                self.__plotWdg.print_(printer)
            elif self.__lib == 'Matplotlib':
                self.__plotWdg.figure.savefig(str(fileName))

    def __outPNG(self):
        """
        To save the profile as png file
        """
        fileName = QFileDialog.getSaveFileName(
            self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"),
            QCoreApplication.translate("VDLTools", "Profile.png"),"Portable Network Graphics (*.png)")
        if fileName is not None:
            QPixmap.grabWidget(self.__printWdg).save(fileName, "PNG")

    def clearData(self):
        """
        To clear the displayed data
        """
        if self.__profiles is None:
            return
        if self.__lib == 'Qwt5':
            self.__plotWdg.clear()
            self.__profiles = None
            temp1 = self.__plotWdg.itemList()
            for j in range(len(temp1)):
                if temp1[j].rtti() == QwtPlotItem.Rtti_PlotCurve:
                    temp1[j].detach()
        elif self.__lib == 'Matplotlib':
            self.__plotWdg.figure.get_axes()[0].cla()
            self.__manageMatplotlibAxe(self.__plotWdg.figure.get_axes()[0])
        self.__maxSpin.setEnabled(False)
        self.__minSpin.setEnabled(False)
        self.__maxSpin.setValue(0)
        self.__minSpin.setValue(0)

        # clear legend
        while self.__legendLayout.count():
            child = self.__legendLayout.takeAt(0)
            child.widget().deleteLater()

    def __manageMatplotlibAxe(self, axe):
        """
        To manage the axes for matplotlib library
        :param axe: the axes element
        """
        axe.grid()
        axe.tick_params(axis="both", which="major", direction="out", length=10, width=1, bottom=True, top=False,
                        left=True, right=False)
        axe.minorticks_on()
        axe.tick_params(axis="both", which="minor", direction="out", length=5, width=1, bottom=True, top=False,
                        left=True, right=False)
        axe.set_xlabel(QCoreApplication.translate("VDLTools", "Distance [m]"))
        axe.set_ylabel(QCoreApplication.translate("VDLTools", "Elevation [m]"))

    def __activateMouseTracking(self, activate):
        """
        To (de)activate the mouse tracking on the profile for matplotlib library
        :param activate: true to activate, false to deactivate
        """
        if activate:
            self.__doTracking = True
            self.__loadRubber()
            self.cid = self.__plotWdg.mpl_connect('motion_notify_event', self.__mouseevent_mpl)
        elif self.__doTracking:
            self.__doTracking = False
            self.__plotWdg.mpl_disconnect(self.cid)
            if self.__marker is not None:
                self.__canvas.scene().removeItem(self.__marker)
            try:
                if self.__vline is not None:
                    self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline)
                    self.__plotWdg.draw()
            except Exception as e:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "Tracking exception : ") + str(e),
                    level=QgsMessageBar.CRITICAL, duration=0)

    def __mouseevent_mpl(self, event):
        """
        To manage matplotlib mouse tracking event
        :param event: mouse tracking event
        """
        if event.xdata is not None:
            try:
                if self.__vline is not None:
                    self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline)
            except Exception as e:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "Mouse event exception : ") + str(e),
                    level=QgsMessageBar.CRITICAL, duration=0)
            xdata = float(event.xdata)
            self.__vline = self.__plotWdg.figure.get_axes()[0].axvline(xdata, linewidth=2, color='k')
            self.__plotWdg.draw()
            i = 1
            while i < len(self.__tabmouseevent)-1 and xdata > self.__tabmouseevent[i][0]:
                i += 1
            i -= 1

            x = self.__tabmouseevent[i][1] + (self.__tabmouseevent[i + 1][1] - self.__tabmouseevent[i][1]) / (
            self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0])
            y = self.__tabmouseevent[i][2] + (self.__tabmouseevent[i + 1][2] - self.__tabmouseevent[i][2]) / (
            self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0])
            self.__marker.show()
            self.__marker.setCenter(QgsPoint(x, y))

    def __loadRubber(self):
        """
        To load te rubber band for mouse tracking on map
        """
        self.__marker = QgsVertexMarker(self.__canvas)
        self.__marker.setIconSize(5)
        self.__marker.setIconType(QgsVertexMarker.ICON_BOX)
        self.__marker.setPenWidth(3)

    def __prepare_points(self):
        """
        To prepare the points on map for mouse tracking on profile
        """
        self.__tabmouseevent = []
        length = 0
        for i, point in enumerate(self.__profiles):
            if i == 0:
                self.__tabmouseevent.append([0, point['x'], point['y']])
            else:
                length += ((self.__profiles[i]['x'] - self.__profiles[i-1]['x']) ** 2 +
                           (self.__profiles[i]['y'] - self.__profiles[i-1]['y']) ** 2) ** 0.5
                self.__tabmouseevent.append([float(length), float(point['x']), float(point['y'])])

    def closeEvent(self, event):
        """
        When the dock widget is closed
        :param event: close event
        """
        if self.__maxSpin is not None:
            Signal.safelyDisconnect(self.__maxSpin.valueChanged, self.__reScalePlot)
            self.__maxSpin = None
        if self.__minSpin is not None:
            Signal.safelyDisconnect(self.__minSpin.valueChanged, self.__reScalePlot)
            self.__minSpin = None
        if self.__saveButton is not None:
            Signal.safelyDisconnect(self.__saveButton.clicked, self.__save)
            self.__saveButton = None
        if self.__libCombo is not None:
            Signal.safelyDisconnect(self.__libCombo.currentIndexChanged, self.__setLib)
            self.__libCombo = None
        self.closeSignal.emit()
        if self.__marker is not None:
            self.__marker.hide()
        QDockWidget.closeEvent(self, event)