class PrettyWidget(QtGui.QWidget): def __init__(self): super(PrettyWidget, self).__init__() self.initUI() def initUI(self): self.setGeometry(600, 300, 1300, 600) self.setFixedSize(1300, 600) self.center() self.setWindowTitle('Health Statistics & Analysis') self.setWindowIcon(QtGui.QIcon('hospital-2.png')) grid = QtGui.QGridLayout() self.setLayout(grid) bar = QtGui.QMenuBar() file = bar.addMenu('File') file.addAction('New') save = QtGui.QAction('Save', self) file.addAction(save) edit = bar.addMenu('Edit') edit.addAction('Copy') edit.addAction('paste') quit = bar.addMenu('Quit') exit = QtGui.QAction('Exit', self) quit.addAction(exit) grid.addWidget(bar, 0, 0, 1, 2) pic = QtGui.QLabel() pic.setPixmap(QtGui.QPixmap('electrocardiogram.png')) pic.resize(pic.sizeHint()) label1 = QtGui.QLabel('Data Manipulation') label1.setFont(QtGui.QFont('Calibri', 11, QtGui.QFont.Bold)) file_import = QtGui.QPushButton('Import File') file_import.resize(file_import.sizeHint()) self.connect(file_import, SIGNAL('clicked()'), self.file_dialog) self.connect(self, SIGNAL('main2closed()'), self.file_clear) button2 = QtGui.QPushButton('Display DataFrame') button2.resize(button2.sizeHint()) self.connect(button2, SIGNAL('clicked()'), self.disp_df) self.connect(self, SIGNAL('main2closed()'), self.df_clear) describ = QtGui.QPushButton('Description') describ.clicked.connect(self.description) sublayout1 = QtGui.QGridLayout() sublayout1.addWidget(pic, 0, 0) sublayout1.addWidget(label1, 1, 0) sublayout1.addWidget(file_import, 2, 0) sublayout1.addWidget(button2, 2, 1) sublayout1.addWidget(describ, 3, 0) grid.addLayout(sublayout1, 1, 0) label2 = QtGui.QLabel('Statistical Visualization') label2.setFont(QtGui.QFont('Calibri', 11, QtGui.QFont.Bold)) button4 = QtGui.QPushButton('Distribution(Histogram)') button4.resize(button4.sizeHint()) self.connect(button4, SIGNAL('clicked()'), self.Hist) self.connect(self, SIGNAL('main2closed()'), self.hist_clear) button5 = QtGui.QPushButton('Time Chart Series') button5.resize(button5.sizeHint()) button5.clicked.connect(self.plot_series) button6 = QtGui.QPushButton('Pie Chart') button6.clicked.connect(self.Pie) button7 = QtGui.QPushButton('Clear Canvas') button7.clicked.connect(self.clear_canvas) sublayout2 = QtGui.QGridLayout() sublayout2.addWidget(label2, 0, 0) sublayout2.addWidget(button4, 1, 0) sublayout2.addWidget(button5, 1, 1) sublayout2.addWidget(button6, 2, 0) sublayout2.addWidget(button7, 2, 1) grid.addLayout(sublayout2, 2, 0, 1, 1) label3 = QtGui.QLabel('Supervised / UnSupervised Learning') label3.setFont(QtGui.QFont('Calibri', 11, QtGui.QFont.Bold)) button7 = QtGui.QPushButton('Decision Tree Classifier') button7.resize(button7.sizeHint()) button7.clicked.connect(self.classifier) self.connect(button7, SIGNAL('clicked()'), self.dc_tree_info) self.connect(self, SIGNAL('main2closed()'), self.clearDecision) self.button8 = QtGui.QPushButton('Clustering(KMean)') self.connect(self.button8, SIGNAL('clicked()'), self.clustering) self.connect(self, SIGNAL('main2closed()'), self.clearCluster) button9 = QtGui.QPushButton('Predictive Learning') self.connect(button9, SIGNAL('clicked()'), self.prediction) self.connect(self, SIGNAL('main2closed()'), self.pred_close) sublayout3 = QtGui.QVBoxLayout() sublayout3.addWidget(label3) sublayout3.addWidget(button7) sublayout3.addWidget(self.button8) sublayout3.addWidget(button9) grid.addLayout(sublayout3, 3, 0, 1, 1) verticalLine = QtGui.QFrame() verticalLine.setFrameStyle(QtGui.QFrame.VLine) verticalLine.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) grid.addWidget(verticalLine, 1, 1, 6, 1) self.figure = plt.figure(figsize=(15, 5)) self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) grid.addWidget(self.canvas, 1, 2, 1, 2) grid.addWidget(self.toolbar, 0, 2, 1, 2) self.show() def disp_df(self): self.display_df = Table_Widget() self.connect(self.display_df, SIGNAL('closed()'), self.df_clear) self.display_df.show() def df_clear(self): self.emit(SIGNAL('main2closed()')) def file_dialog(self): self.file_diag = file_dialog() self.connect(self.file_diag, SIGNAL('closed()'), self.hist_close) self.file_diag.show() def file_clear(self): self.emit(SIGNAL('main2closed()')) def file_import(self): print('file_import') deef = file_dialog deef.get_csv_filename(self) return deef #data description mean, std, count, min, max, quartile, median. def description(self): df = pd.read_csv('2015 data.csv') fig, ax = plt.subplots(figsize=(13, 3)) # set size frame ax.xaxis.set_visible(False) # hide the x axis ax.yaxis.set_visible(False) # hide the y axis ax.set_frame_on(False) # no visible frame, uncomment if size is ok #tabla = table(ax, df.describe(), loc='center') # where df is your data frame #tabla.auto_set_font_size(False) # Activate set fontsize manually #tabla.set_fontsize(12) # if ++fontsize is necessary ++colWidths #tabla.scale(1.2, 1.2) # change size table plt.show() #plot time curve series for the trend of Malaria def plot_series(self): time_plot() #plot pie chart to represent the fraction of Gender and Malaria cases from a Whole. def Pie(self): self.pie_window = Pie_Distr() self.connect(self.hist_window, SIGNAL('closed()'), self.pie_close) self.pie_window.show() def pie_close(self): self.emit(SIGNAL('main2closed()')) def pie_clear(self): del self.pie_window self.pie_window = None def clear_canvas(self): self.canvas.clear() def plot1(self): plt.cla() ax = self.figure.add_subplot(111) x = [i for i in range(100)] y = [i**2 for i in x] ax.plot(x, y, 'b.-') ax.set_title('Quadratic Plot') self.canvas.draw() def plot2(self): self.canvas.clearFocus() plt.cla() ax = self.figure.add_subplot(111) x = [i for i in range(100)] y = [i**0.5 for i in x] ax.plot(x, y, 'r.-') ax.set_title('Square Root Plot') self.canvas.draw() def plot3(self): return 0 '''To plot the distribution of health from data gathered; Histogram will determine Age, Height & Weight Distribution''' def Hist(self): self.hist_window = Hist_Distr() self.connect(self.hist_window, SIGNAL('closed()'), self.hist_close) self.hist_window.show() def hist_close(self): self.emit(SIGNAL('main2closed()')) def hist_clear(self): del self.hist_window self.hist_window = None def classifier(self): print("\n-- get data:") df = get_iris_data() print("\n-- df.head():") #print(df.head(), end="\n\n") features = ["Age", "Height", "Weight", "BMI"] df, targets = encode_target(df, "Malaria") #Learning of the algorithm y = df["Target"] X = df[features] #dt = DecisionTreeClassifier(min_samples_split=20, random_state=99) #dt.fit(X, y) print("\n-- get_code:") get_code(dt, features, targets) print("\n-- Target Class") #print(df[df['Weight'] <= 50]['Malaria'].unique(), end="\n\n") #visualize_tree(dt, features) def dc_tree_info(self): self.decision_details = d_tree_info() self.connect(self.decision_details, SIGNAL('closed()'), self.decision_close) self.decision_details.show() def decision_close(self): self.emit(SIGNAL('main2closed()')) def clearDecision(self): del self.decision_details self.decision_details = None #Unspervised learning where the algorithm trys to group the data from the dataset, based on the def clustering(self): self.other_window = clustered_layout() self.connect(self.other_window, SIGNAL('closed()'), self.clustering_close) self.other_window.show() def clustering_close(self): self.emit(SIGNAL('main2closed()')) def clearCluster(self): del self.other_window self.other_window = None #To carry out the prediction of target class representing Malaria and its Types based on # the characteristics they potray from the data collected. def prediction(self): self.pred_window = prediction() self.connect(self.pred_window, SIGNAL('closed()'), self.pred_close) self.pred_window.show() def pred_close(self): self.emit(SIGNAL('main2closed')) def clearPred(self): del self.pred_window self.pred_window = None #position the main window in the center position of the screen layout. def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft())
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)
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)