def setSceneRect(self, rect): QGraphicsScene.setSceneRect(self, rect) h = rect.height() - self.yOffset * 2 if self.zMax != self.zMin: self.zScale = h / (self.zMax - self.zMin) else: self.zScale = 1.0
def displayPlot(self): QGraphicsScene.clear(self) self.sceneRect() fm = QFontMetrics(QFont()) # display lines fitting in sceneRect last = None for z1, z2, text in self.data: brush = QBrush( QColor.fromHsl(z2 / (self.zMax - self.zMin) * 360.0, 128, 128) ) self.addRect( self.xOffset + self.legendWidth, z1 * self.zScale + self.yOffset, self.barWidth, (z2 - z1) * self.zScale, QPen(), brush, ) if last is None: legend_item = self.addSimpleText("{}".format(z1)) legend_item.setPos(self.xOffset, z1 * self.zScale + self.yOffset) legend_item = self.addSimpleText("{}".format(z2)) legend_item.setPos(self.xOffset, z2 * self.zScale + self.yOffset) last = z2 text_item = self.addSimpleText(text) text_item.setPos( self.xOffset * 2 + self.legendWidth + self.barWidth, (z1 + z2) / 2.0 * self.zScale - fm.height() / 2.0 + self.yOffset, )
def clear(self): QGraphicsScene.clear(self) self.data = [] self.xMin = 0 self.xMax = 0 self.yMin = 0 self.yMax = 0
def show_image(self, path, flags=Qt.KeepAspectRatioByExpanding): pic = QPixmap(path) grview = ImageViewClass(origPixmap=pic) scene = QGraphicsScene() scene.addPixmap(pic) grview.setScene(scene) self.gridLayout_2.addWidget(grview)
def clear(self): QGraphicsScene.clear(self) self.data = [] self.zMin = 0 self.zMax = 0 self.width = 0 self.zScale = 100.0 self.legendWidth = 0 self.xOffset = 5 self.yOffset = 20
def __init__(self, parent): QGraphicsScene.__init__(self, parent) self.zScale = 100.0 # width (in pixels) of the bar self.barWidth = 20 # offset from the left border self.xOffset = 10 self.clear()
def setSceneRect(self, rect): QGraphicsScene.setSceneRect(self, rect) w = rect.width() - self.barWidth if self.xMax != self.xMin: self.xRatio = w / (self.xMax - self.xMin) else: self.xRatio = 1.0 h = rect.height() - self.yOffset * 2 if self.yMax != self.yMin: self.yRatio = h / (self.yMax - self.yMin) else: self.yRatio = 1.0
def run(self): """Run method that loads and starts the plugin""" if not self.pluginIsActive: self.pluginIsActive = True self.scene = QGraphicsScene() # Storing the spectra to be able to read out values later on # Setting the values storing line and text shown when the mouse button is clicked self.scene.crdtext = None self.scene.markerline = None self.scene.left = None # TODO: The four next settings to be user-settable self.tickinterval = 100 s = QgsSettings() self.scene.acalib = s.value(self.pluginname + "/defaulta", 1) self.scene.bcalib = s.value(self.pluginname + "/defaultb", 0) self.scene.unit = s.value(self.pluginname + "/defaultunit", "Ch") self.dlg.leA.setText(str(self.scene.acalib)) self.dlg.leB.setText(str(self.scene.bcalib)) self.dlg.leUnit.setText(str(self.scene.unit)) showch = False # Set to True to show channel values if showch: self.scene.unit = 'Ch' self.scene.acalib = 1 self.scene.bcalib = 0 self.view.setScene(self.scene) self.scene.setSceneRect(0, 0, 1200, 300) # Replotting spectre when a new selection is made self.iface.mapCanvas().selectionChanged.connect(self.findselected) # Listing layers # DONE: Only list vector layers # DONE: Repopulate when layers are added or removed # DONE both by using qgisWidget self.dlg.pBCopy.clicked.connect(self.spectreToClipboard) self.dlg.pBUseDefault.clicked.connect(self.usedefault) self.dlg.pBSaveCalib.clicked.connect(self.saveCalibration) self.dlg.pBSave.clicked.connect(self.view.saveImage) # connect to provide cleanup on closing of dockwidget self.dlg.closingPlugin.connect(self.onClosePlugin) # show the dockwidget self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self.dlg) self.dlg.show() self.dlg.cbLog.stateChanged.connect(self.findselected) self.dlg.cbDefault.stateChanged.connect(self.setdefault) self.dlg.qgField.currentIndexChanged['QString'].connect( self.prepareplot) self.dlg.qgLayer.currentIndexChanged['QString'].connect( self.prepareplot) self.dlg.leA.textChanged['QString'].connect(self.updatecalib) self.dlg.leB.textChanged['QString'].connect(self.updatecalib) self.findselected()
def displayPlot(self): QGraphicsScene.clear(self) self.marker.clear() self.sceneRect() # display lines fitting in sceneRect poly = QPolygonF() for x, y, _ in self.data: poly.append(QPointF(self.xToScene(x), self.yToScene(y))) # close the polygon x2 = self.xToScene(self.xMax) y2 = self.sceneRect().height() poly.append(QPointF(x2, y2)) x2 = self.barWidth poly.append(QPointF(x2, y2)) x2 = self.xToScene(self.xMin) y2 = self.yToScene(0) poly.append(QPointF(x2, y2)) brush = QBrush(QColor("#DCF1F7")) pen = QPen() pen.setWidth(0) self.addPolygon(poly, pen, brush) # horizontal line on ymin and ymax self.addLine( self.barWidth - 5, self.yToScene(self.yMin), self.barWidth + 5, self.yToScene(self.yMin), ) self.addLine( self.barWidth - 5, self.yToScene(self.yMax), self.barWidth + 5, self.yToScene(self.yMax), ) # display scale self.addLine(self.barWidth, 0, self.barWidth, self.sceneRect().height()) font = QFont() fm = QFontMetrics(font) t1 = self.addText("%.1f" % self.yMin) t1.setPos(0, self.yToScene(self.yMin) - fm.ascent()) t2 = self.addText("%.1f" % self.yMax) t2.setPos(0, self.yToScene(self.yMax) - fm.ascent()) # Z(m) t3 = self.addText(self.yTitle) t3.setPos(0, 0)
def __init__(self, yTitle, parent): QGraphicsScene.__init__(self, parent) # width of the scale bar fm = QFontMetrics(QFont()) # width = size of "100.0" with the default font + 10% self.yTitle = yTitle self.barWidth = max(fm.width(yTitle), fm.width("000.00")) self.xOffset = self.barWidth self.yOffset = fm.height() * 2 # define the transformation between distance, altitude and scene coordinates self.xRatio = 1.0 self.yRatio = 1.0 self.marker = PointMarker(self) self.clear()
def mouseMoveEvent(self, event): # pass the event to the underlying item for item in list(self.items()): r = item.boundingRect() r.translate(item.pos()) if r.contains(event.scenePos()): return item.mouseMoveEvent(event) return QGraphicsScene.mouseMoveEvent(self, event)
def __init__(self, parent=None): super(HelpPage, self).__init__(parent) self.setupUi(self) if self.parent(): self.parent().installEventFilter(self) self.view = QGraphicsView() self.webView = QGraphicsWebView() self.scene = QGraphicsScene() self.webView.setResizesToContents(True) self.view.setScene(self.scene) self.scene.addItem(self.webView) self.webView.linkClicked.connect(RoamEvents.openurl.emit) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) install_touch_scroll(self.view) self.layout().addWidget(self.view)
class HelpPage(helppage_widget, helppage_base): def __init__(self, parent=None): super(HelpPage, self).__init__(parent) self.setupUi(self) if self.parent(): self.parent().installEventFilter(self) self.view = QGraphicsView() self.webView = QGraphicsWebView() self.scene = QGraphicsScene() self.webView.setResizesToContents(True) self.view.setScene(self.scene) self.scene.addItem(self.webView) self.webView.linkClicked.connect(RoamEvents.openurl.emit) self.webView.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) install_touch_scroll(self.view) self.layout().addWidget(self.view) def eventFilter(self, object, event): if event.type() == QEvent.Resize: self.setsize() elif event.type() == QEvent.MouseButtonPress: self.close() return object.eventFilter(object, event) def setsize(self): width = self.parent().width() * 50 / 100 self.resize(width, self.parent().height()) self.move(self.parent().width() - self.width() - 1, 1) def show(self): super(HelpPage, self).show() self.setsize() self.setFocus(Qt.PopupFocusReason) def setHelpPage(self, helppath): self.webView.load(QUrl.fromLocalFile(helppath))
def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/stripchart/icon.png' self.add_action(icon_path, text=self.tr(u'Stripchart'), callback=self.run, parent=self.iface.mainWindow()) self.dlg = StripChartDockWidget(self.iface.mainWindow()) self.view.setParent(self.dlg) self.dlg.vlMain.addWidget(self.view) self.scene = QGraphicsScene() self.view.setScene(self.scene) self.scene.setSceneRect(0, 0, 300, 2000) self.dlg.qgLayer.setFilters(QgsMapLayerProxyModel.VectorLayer) self.dlg.qgLayer.setLayer(self.iface.activeLayer()) self.dlg.qgField.setAllowEmptyFieldName(True) self.dlg.qgField.setLayer(self.dlg.qgLayer.currentLayer()) self.dlg.qgField.setFilters(QgsFieldProxyModel.Numeric) self.dlg.qgLayer.layerChanged.connect(self.selectedlayer) self.iface.mapCanvas().selectionChanged.connect(self.markselected) self.dlg.qgField.currentIndexChanged['QString'].connect( self.stripchart)
def altplot(self, dataset, graphicsview): w = graphicsview.width() h = graphicsview.height() air = 39 #bt=airborne plotw = w - air * 2 ploth = h - air * 2 scene = QGraphicsScene() graphicsview.setScene(scene) xspan = [min(dataset[0]), max(dataset[0])] yspan = [min(dataset[1]), max(dataset[1])] xfact = (xspan[1] - xspan[0]) / plotw yfact = (yspan[1] - yspan[0]) / ploth rad = 2 xaxy = float(ploth + rad * 2) yaxx = float(air - 1) scene.addLine(yaxx, xaxy, yaxx, air / 2) # Y-axis scene.addLine(yaxx, xaxy, float(w - air / 2), xaxy) # X-axis for alt, meas in zip(dataset[0], dataset[1]): x = (alt - xspan[0]) / xfact + air y = ploth - (meas - yspan[0]) / yfact scene.addEllipse(x, y, rad * 2, rad * 2)
def __init__(self, model, parent): QGraphicsScene.__init__(self, parent) tables_coords = [] spiral = spiral_iterator() min_grid_x = None max_grid_x = None min_grid_y = None max_grid_y = None self.table_items = {} for table_name, table in model.tables().items(): # widget, grid_x, grid_y, x, y grid_x, grid_y = next(spiral) if grid_x > max_grid_x or max_grid_x is None: max_grid_x = grid_x if grid_x < min_grid_x or min_grid_x is None: min_grid_x = grid_x if grid_y > max_grid_y or max_grid_y is None: max_grid_y = grid_y if grid_y < min_grid_y or min_grid_y is None: min_grid_y = grid_y w = TableWidget(table) w.linkActivated.connect(self.tableSelected) tw = self.addWidget(w) tables_coords.append((tw, grid_x, grid_y, 0, 0, table_name)) self.table_items[table_name] = tw # resolve columns / rows size column_width = {} row_height = {} for table, grid_x, grid_y, x, y, _ in tables_coords: wmax = column_width.get(grid_x) hmax = row_height.get(grid_y) wsize = table.widget().size() if wsize.width() > wmax or wmax is None: column_width[grid_x] = wsize.width() if wsize.height() > hmax or hmax is None: row_height[grid_y] = wsize.height() # total_width = sum(column_width.values()) # total_height = sum(row_height.values()) # resolve coordinates column_x = {} row_y = {} w = 0 for x in range(min_grid_x, max_grid_x + 1): column_x[x] = w w += column_width[x] h = 0 for y in range(min_grid_y, max_grid_y + 1): row_y[y] = h h += row_height[y] table_pos = {} for i in range(len(tables_coords)): table, grid_x, grid_y, _, _, table_name = tables_coords[i] wsize = table.widget().size() x = column_x[grid_x] + (column_width[grid_x] - wsize.width()) / 2.0 y = row_y[grid_y] + (row_height[grid_y] - wsize.height()) / 2.0 tables_coords[i] = (table, x, y) table_pos[table_name] = (x, y, wsize.width(), wsize.height(), table) table.setPos(x, y) def distance(p1, p2): l = QLineF(p1, p2) return l.length() # display links arrows def add_link(table_name, table_name2, y_idx): tx, ty, tw, th, table = table_pos[table_name] tx2, ty2, tw2, th2, _ = table_pos[table_name2] (ax, ay, aw, ah) = table.widget().attributeCoords(y_idx) l1 = QLineF(ax - 3, ay + ah / 2.0, tx2 + tw2 / 2.0, ty2 + th2 / 2.0) l2 = QLineF(ax + aw + 3, ay + ah / 2.0, tx2 + tw2 / 2.0, ty2 + th2 / 2.0) if l1.length() < l2.length(): p1 = l1.p1() l = l1 else: p1 = l2.p1() l = l2 # add a diamond ds = 6 dbrush = QBrush(QColor("black")) diamond = QPolygonF( [ p1 + QPointF(0, ds), p1 + QPointF(ds, 0), p1 + QPointF(0, -ds), p1 + QPointF(-ds, 0), p1 + QPointF(0, ds), ] ) i1 = self.addPolygon(diamond, QPen(), dbrush) # cross with the second table widget points = [ horizontal_intersection(l, ty2, tx2, tx2 + tw2), horizontal_intersection(l, ty2 + th2, tx2, tx2 + tw2), vertical_intersection(l, tx2, ty2, ty2 + th2), vertical_intersection(l, tx2 + tw2, ty2, ty2 + th2), ] pp = [p for p in points if p is not None] p2 = min(pp, key=lambda p: distance(p1, p)) l = QLineF(p1, p2) i2 = self.addLine(l) i2.setZValue(1) alpha = math.atan2(p2.y() - p1.y(), p2.x() - p1.x()) alpha1 = alpha + 30.0 / 180.0 * math.pi alpha2 = alpha - 30.0 / 180.0 * math.pi r = -10 p3 = QPointF(r * math.cos(alpha1), r * math.sin(alpha1)) + p2 p4 = QPointF(r * math.cos(alpha2), r * math.sin(alpha2)) + p2 i3 = self.addLine(QLineF(p2, p3)) i4 = self.addLine(QLineF(p2, p4)) return [i1, i2, i3, i4] for table_name, table in model.tables().items(): tw = self.table_items[table_name] y = len(table.columns()) items = [tw] for l in table.links(): if l.max_occurs() != 1: continue items += add_link(table_name, l.ref_table().name(), y) y += 1 for l in table.back_links(): items += add_link(table_name, l.ref_table().name(), y) y += 1 self.table_items[table_name] = items # make items invisible for now for item in items[1:]: disable_link_item(item)
def __init__(self, x, y, w, h): QGraphicsScene.__init__(self, x, y, w, h)
def __init__(self, iface, parent=None): """Constructor.""" self.iface = iface super(BOSDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) # Some constants for translated text self.BOS = self.tr('BOS') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.HELP = self.tr('Help') self.CLOSE = self.tr('Close') self.OK = self.tr('OK') #self.NUMBEROFSTEPS = 10 # Number of steps self.labelTextSize = 8 self.titleTextSize = 10 self.INPUT = 'input' self.REF = 'reference' okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) helpButton = self.helpButton helpButton.setText(self.HELP) self.BOSscene = QGraphicsScene(self) self.BOSGraphicsView.setScene(self.BOSscene) # Connect signals okButton.clicked.connect(self.startWorker) #self.displacementButton.clicked.connect(self.showPlots) #self.avgdispButton.clicked.connect(self.showAverageDisplacement) #self.oscillationButton.clicked.connect(self.showOscillation) #self.complmiscButton.clicked.connect(self.showComplenessMiscoding) #self.saveSvgButton.clicked.connect(self.saveAsSVG) #self.saveAsPdfButton.clicked.connect(self.saveAsPDF) # Global variables with mutexes # Dictionary variable for input buffers self.inputbuffers = {} self.ibmutex = Lock() # Dictionary variable for reference buffers self.referencebuffers = {} self.rbmutex = Lock() # Variables for statistics # Number of polygons: self.polycount = {} self.pcmutex = Lock() # Complenetess values: self.completeness = {} self.comutex = Lock() # Miscoding values: self.miscodings = {} self.mimutex = Lock() # Other statistics self.statistics = {} self.stmutex = Lock() self.testcounter = 0 self.tcmutex = Lock() self.radiuses = [] # This one fires "immediately"? QgsApplication.taskManager().allTasksFinished.connect( self.all_tasks_completed)
def __init__(self, iface, parent=None): """Constructor.""" self.iface = iface self.plugin_dir = dirname(__file__) self.THINGREYSCALE = self.tr('ThinGreyscale') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.CLOSE = self.tr('Close') self.HELP = self.tr('Help') self.OK = self.tr('OK') self.DEFAULTPROVIDER = 'GTiff' self.DEFAULTEXTENSION = '.tif' self.EXTRAEXTENSION = ' *.tiff' super(ThinGreyscaleDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.showInfo("Connecting UI components") okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) browseButton = self.browseButton browseButton.setText(self.BROWSE) self.calcHistPushButton.setEnabled(False) self.listModel = QStandardItemModel(self.levelsListView) self.levelsListView.setModel(self.listModel) self.levelsListView.sizeHintForColumn(20) #self.levelValuesCheckBox.setEnabled(False) # Help button helpButton = self.helpButton helpButton.setText(self.HELP) # Connect signals self.showInfo("Connecting signals") okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) closeButton.clicked.connect(self.reject) helpButton.clicked.connect(self.help) browseButton.clicked.connect(self.browse) inpIndexCh = self.inputRaster.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) bandCh = self.bandComboBox.currentIndexChanged['QString'] bandCh.connect(self.bandChanged) #self.iface.legendInterface().itemAdded.connect( # self.layerlistchanged) #self.iface.legendInterface().itemRemoved.connect( # self.layerlistchanged) #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject) self.button_box.rejected.disconnect(self.reject) calchistPr = self.calcHistPushButton.clicked calchistPr.connect(self.calculateHistogram) sugglevPr = self.suggestlevelsPushButton.clicked sugglevPr.connect(self.suggestLevels) addlevPr = self.addlevelPushButton.clicked addlevPr.connect(self.addLevel) dellevPr = self.deletelevelsPushButton.clicked dellevPr.connect(self.removeLevel) maxvalCh = self.maxValueSpinBox.valueChanged maxvalCh.connect(self.minmaxvalueChanged) maxvalFi = self.maxValueSpinBox.editingFinished maxvalFi.connect(self.minmaxvalueEdFinished) minvalCh = self.minValueSpinBox.valueChanged minvalCh.connect(self.minmaxvalueChanged) minvalFi = self.minValueSpinBox.editingFinished minvalFi.connect(self.minmaxvalueEdFinished) # Set instance variables #self.mem_layer = None self.worker = None self.inputlayerid = None self.inputlayer = None self.layerlistchanging = False self.minvalue = 1 self.inputrasterprovider = None self.histobins = 50 self.setupScene = QGraphicsScene(self) self.histoGraphicsView.setScene(self.setupScene) # Is the layer band of an integer type self.intband = False self.histogramAvailable = False self.histo = None self.histopadding = 1
class ThinGreyscaleDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" self.iface = iface self.plugin_dir = dirname(__file__) self.THINGREYSCALE = self.tr('ThinGreyscale') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.CLOSE = self.tr('Close') self.HELP = self.tr('Help') self.OK = self.tr('OK') self.DEFAULTPROVIDER = 'GTiff' self.DEFAULTEXTENSION = '.tif' self.EXTRAEXTENSION = ' *.tiff' super(ThinGreyscaleDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.showInfo("Connecting UI components") okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) browseButton = self.browseButton browseButton.setText(self.BROWSE) self.calcHistPushButton.setEnabled(False) self.listModel = QStandardItemModel(self.levelsListView) self.levelsListView.setModel(self.listModel) self.levelsListView.sizeHintForColumn(20) #self.levelValuesCheckBox.setEnabled(False) # Help button helpButton = self.helpButton helpButton.setText(self.HELP) # Connect signals self.showInfo("Connecting signals") okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) closeButton.clicked.connect(self.reject) helpButton.clicked.connect(self.help) browseButton.clicked.connect(self.browse) inpIndexCh = self.inputRaster.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) bandCh = self.bandComboBox.currentIndexChanged['QString'] bandCh.connect(self.bandChanged) #self.iface.legendInterface().itemAdded.connect( # self.layerlistchanged) #self.iface.legendInterface().itemRemoved.connect( # self.layerlistchanged) #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject) self.button_box.rejected.disconnect(self.reject) calchistPr = self.calcHistPushButton.clicked calchistPr.connect(self.calculateHistogram) sugglevPr = self.suggestlevelsPushButton.clicked sugglevPr.connect(self.suggestLevels) addlevPr = self.addlevelPushButton.clicked addlevPr.connect(self.addLevel) dellevPr = self.deletelevelsPushButton.clicked dellevPr.connect(self.removeLevel) maxvalCh = self.maxValueSpinBox.valueChanged maxvalCh.connect(self.minmaxvalueChanged) maxvalFi = self.maxValueSpinBox.editingFinished maxvalFi.connect(self.minmaxvalueEdFinished) minvalCh = self.minValueSpinBox.valueChanged minvalCh.connect(self.minmaxvalueChanged) minvalFi = self.minValueSpinBox.editingFinished minvalFi.connect(self.minmaxvalueEdFinished) # Set instance variables #self.mem_layer = None self.worker = None self.inputlayerid = None self.inputlayer = None self.layerlistchanging = False self.minvalue = 1 self.inputrasterprovider = None self.histobins = 50 self.setupScene = QGraphicsScene(self) self.histoGraphicsView.setScene(self.setupScene) # Is the layer band of an integer type self.intband = False self.histogramAvailable = False self.histo = None self.histopadding = 1 def startWorker(self): """Initialises and starts the worker thread.""" try: layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) inputlayer = QgsProject.instance().mapLayer(layerId) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) if inputlayer is None: self.showError(self.tr('No input layer defined')) return # create a reference to the layer that is being processed # (for use when creating the resulting raster layer) self.thinninglayer = inputlayer self.levels = [] #self.levelsListView.selectAll() #selected = self.levelsListView.selectedIndexes() if self.levelsListView.model().rowCount() == 0: self.showInfo("Levels must be specified!") return for i in range(self.levelsListView.model().rowCount()): levelstring = self.levelsListView.model().item(i).text() #for i in selected: # levelstring = self.levelsListView.model().itemData(i)[0] if self.intband: self.levels.append(int(levelstring)) else: self.levels.append(float(levelstring)) #self.levelsListView.clearSelection() # create a new worker instance worker = Worker(inputlayer, self.levels, self.intband) # configure the QgsMessageBar msgBar = self.iface.messageBar().createMessage( self.tr('Skeletonising'), '') self.aprogressBar = QProgressBar() self.aprogressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) acancelButton = QPushButton() acancelButton.setText(self.CANCEL) acancelButton.clicked.connect(self.killWorker) msgBar.layout().addWidget(self.aprogressBar) msgBar.layout().addWidget(acancelButton) # Has to be popped after the thread has finished (in # workerFinished). self.iface.messageBar().pushWidget(msgBar, Qgis.Info) self.messageBar = msgBar # start the worker in a new thread thread = QThread(self) worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) worker.status.connect(self.workerInfo) worker.progress.connect(self.progressBar.setValue) worker.progress.connect(self.aprogressBar.setValue) worker.iterprogress.connect(self.iterProgressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Close).setEnabled(False) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) except: import traceback self.showError(traceback.format_exc()) else: pass def workerFinished(self, ok, ret): """Handles the output from the worker and cleans up after the worker has finished.""" # clean up the worker and thread self.showInfo("Handling the result") self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() # remove widget from message bar (pop) self.iface.messageBar().popWidget(self.messageBar) if ok and ret is not None: #self.showInfo("Ret: "+str(ret[10,])) # Transformation: self.minx = self.thinninglayer.extent().xMinimum() self.maxx = self.thinninglayer.extent().xMaximum() self.miny = self.thinninglayer.extent().yMinimum() self.maxy = self.thinninglayer.extent().yMaximum() self.rows = self.thinninglayer.height() self.cols = self.thinninglayer.width() self.xres = (self.maxx - self.minx) / float(self.cols) self.yres = (self.maxy - self.miny) / float(self.rows) geotransform = (self.minx, self.xres, 0, self.maxy, 0, -self.yres) try: format = self.DEFAULTPROVIDER driver = gdal.GetDriverByName(format) NOVALUE = 0 metadata = driver.GetMetadata() fileName = self.outputRaster.text() if self.outputRaster.text() == "": self.showInfo("No output file specified, " + "creating a temporary file") # Get a temporary file fileName = mktemp(prefix='greyskel', suffix=self.DEFAULTEXTENSION) fileInfo = QFileInfo(fileName) filepath = fileInfo.absolutePath() baseName = fileInfo.baseName() suffix = fileInfo.suffix() thisfilename = filepath + baseName + '.' + suffix thisfilename = fileName self.showInfo("File name: " + thisfilename) gdaldatatype = gdal.GDT_Byte skelmatrix = None if self.levelValuesCheckBox.isChecked(): # Transform the pixel values back to the original # level values my_dict = {} # Add zero to handle the "empty" pixels my_dict[0] = 0 for i in range(len(self.levels)): my_dict[i + 1] = self.levels[i] skelmatrix = np.vectorize(my_dict.__getitem__, otypes=[np.float])(ret) gdaldatatype = gdal.GDT_Int32 if not self.intband: gdaldatatype = gdal.GDT_Float32 else: skelmatrix = ret outDataset = driver.Create(thisfilename, self.cols, self.rows, 1, gdaldatatype) if self.thinninglayer.dataProvider().crs() is not None: srs = self.thinninglayer.dataProvider().crs() outDataset.SetProjection(srs.toWkt().encode('ascii', 'ignore')) skeletonband = outDataset.GetRasterBand(1) skeletonband.WriteArray(skelmatrix) skeletonband.SetNoDataValue(NOVALUE) #stats = skeletonband.GetStatistics(False, True) #skeletonband.SetStatistics(stats[0], stats[1], # stats[2], stats[3]) outDataset.SetGeoTransform(geotransform) outDataset = None # To close the file # report the result rlayer = QgsRasterLayer(thisfilename, baseName) self.layerlistchanging = True #QgsMapLayerRegistry.instance().addMapLayer(rlayer) QgsProject.instance().addMapLayer(rlayer) self.layerlistchanging = False except: import traceback self.showError("Can't write the skeleton file: %s" % self.outputRaster.text() + ' - ' + traceback.format_exc()) okb = self.button_box.button(QDialogButtonBox.Ok) okb.setEnabled(True) closb = self.button_box.button(QDialogButtonBox.Close) closb.setEnabled(True) cancb = self.button_box.button(QDialogButtonBox.Cancel) cancb.setEnabled(False) return QgsMessageLog.logMessage(self.tr('ThinGreyscale finished'), self.THINGREYSCALE, Qgis.Info) else: # notify the user that something went wrong if not ok: self.showError(self.tr('Aborted') + '!') else: self.showError(self.tr('No skeleton created') + '!') self.progressBar.setValue(0.0) #self.aprogressBar.setValue(0.0) self.iterProgressBar.setValue(0.0) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Close).setEnabled(True) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False) def workerError(self, exception_string): """Report an error from the worker.""" #QgsMessageLog.logMessage(self.tr('Worker failed - exception') + # ': ' + str(exception_string), # self.THINGREYSCALE, # QgsMessageLog.CRITICAL) self.showError(exception_string) def workerInfo(self, message_string): """Report an info message from the worker.""" QgsMessageLog.logMessage(self.tr('Worker') + ': ' + message_string, self.THINGREYSCALE, Qgis.Info) def layerchanged(self, number=0): """Do the necessary updates after a layer selection has been changed.""" self.showInfo("Layer changed") # If the layer list is being updated, don't do anything if self.layerlistchanging: return layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) self.inputlayerid = layerId #self.inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) self.inputlayer = QgsProject.instance().mapLayer(layerId) if self.inputlayer is not None: self.inputrasterprovider = self.inputlayer.dataProvider() self.bandComboBox.clear() bandcount = self.inputlayer.bandCount() #self.showInfo("Layer bandcount: "+str(bandcount)) for i in range(bandcount): self.bandComboBox.addItem(self.inputlayer.bandName(i + 1), i) #self.showInfo("Band " + str(i) + ": " + # self.inputlayer.bandName(i+1)) # Check if the driver supports Create() or CreateCopy() #gdalmetadata = self.inputlayer.metadata() #self.showInfo("Layer metadata: " + # str(gdalmetadata.encode('utf-8'))) #provstring = '<p>GDAL provider</p>\n' #providerpos = gdalmetadata.find(provstring) #brpos = gdalmetadata.find('<br>', providerpos + len(provstring)) #self.gdalprovider = gdalmetadata[int(providerpos + # len(provstring)):int(brpos)] #self.showInfo('GDAL provider: '+self.gdalprovider) #drivername = self.gdalprovider.encode('ascii', 'ignore') #theDriver = gdal.GetDriverByName(drivername) #if theDriver is None: # self.showInfo("Unable to get the raster driver") #else: #data theMetadata = theDriver.GetMetadata() #self.showInfo("Driver metadata: "+str(theMetadata)) #if ((gdal.DCAP_CREATE in theMetadata) and # theMetadata[gdal.DCAP_CREATE] == 'YES'): # self.canCreate = True #if (theMetadata.has_key(gdal.DCAP_CREATECOPY) and #if ((gdal.DCAP_CREATECOPY in theMetadata) and # theMetadata[gdal.DCAP_CREATECOPY] == 'YES'): # self.canCreateCopy = True #self.showInfo('raster provider type: ' + # str(self.inputlayer.providerType())) # Determine the file suffix #self.gdalext = "" #if gdal.DMD_EXTENSION in theMetadata: # self.gdalext = "." + theMetadata[gdal.DMD_EXTENSION] #else: # self.showInfo("No extension available in GDAL metadata") # by parsing the layer metadata looking for # "Dataset Description" #descstring = 'Dataset Description</p>\n<p>' #descpos = gdalmetadata.find(descstring) #ppos = gdalmetadata.find('</p>',descpos+len(descstring)) #filename = gdalmetadata[descpos+len(descstring):ppos] #self.gdalext = splitext(filename)[1] #self.showInfo('GDAL extension: '+self.gdalext) # Determine the datatype #datatypestring = 'Data Type</p>\n<p>' #datatypepos = gdalmetadata.find(datatypestring) #ppos = gdalmetadata.find('</p>', # datatypepos + len(datatypestring)) #datatypedesc = gdalmetadata[datatypepos + # len(datatypestring):ppos] #shortdesc = datatypedesc.split()[0] #self.showInfo('GDAL data type: GDT_'+shortdesc) # Call the findGdalDatatype function #self.findGdalDatatype(shortdesc) # self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.calcHistPushButton.setEnabled(True) self.suggestlevelsPushButton.setEnabled(True) def bandChanged(self): band = self.bandComboBox.currentIndex() + 1 self.showInfo("Band changed: " + str(band)) statistics = self.inputrasterprovider.bandStatistics(band) #self.showInfo("Band statistics: " + str(statistics.minimumValue) + # " - " + str(statistics.maximumValue) + # " - " + str(statistics.mean)) self.bandmin = statistics.minimumValue self.bandmax = statistics.maximumValue dt = self.inputrasterprovider.dataType(band) # Integer data type if (dt == Qgis.Byte or dt == Qgis.UInt16 or dt == Qgis.Int16 or dt == Qgis.UInt32 or dt == Qgis.Int32): self.intband = True self.minValueSpinBox.setDecimals(0) self.maxValueSpinBox.setDecimals(0) self.levelSpinBox.setDecimals(0) self.bandMinLabel.setText(str(int(statistics.minimumValue))) self.bandMaxLabel.setText(str(int(statistics.maximumValue))) else: self.intband = False self.minValueSpinBox.setDecimals(5) self.maxValueSpinBox.setDecimals(5) self.levelSpinBox.setDecimals(5) minlabtext = "{0:.5f}".format(statistics.minimumValue) self.bandMinLabel.setText(minlabtext) maxlabtext = "{0:.5f}".format(statistics.maximumValue) self.bandMaxLabel.setText(maxlabtext) #self.minValueSpinBox.setMinimum(statistics.minimumValue) self.maxValueSpinBox.setMinimum(statistics.minimumValue) #self.minValueSpinBox.setMaximum(statistics.maximumValue) self.maxValueSpinBox.setMaximum(statistics.maximumValue) #self.minValueSpinBox.setValue(statistics.minimumValue) if not (statistics.statsGathered & statistics.Mean): bandmean = (statistics.minimumValue + statistics.maximumValue) / 2 else: #self.showInfo("statsgathered: " + str(statistics.statsGathered)) bandmean = statistics.mean if self.intband: self.minValueSpinBox.setValue(int(ceil(bandmean))) else: self.minValueSpinBox.setValue(bandmean) self.maxValueSpinBox.setValue(statistics.maximumValue) self.histMinValue.setText(str(statistics.minimumValue)) self.histMaxValue.setText(str(statistics.maximumValue)) self.levelSpinBox.setMinimum(statistics.minimumValue) self.levelSpinBox.setMaximum(statistics.maximumValue) self.histogramAvailable = False #if self.inputrasterprovider.hasStatistics(band): #if statistics.statsGathered: #histogram = statistics.histogramVector #self.showInfo("Histogram: " + str(histogram)) #range = min to max #np.histogram(band, 50, range) def minmaxvalueChanged(self): #if self.minValueSpinBox is None: # return minvalue = self.minValueSpinBox.value() #if minvalue is None: # return #if self.maxValueSpinBox is None: # return maxvalue = self.maxValueSpinBox.value() #if maxvalue is None: # return if isnan(maxvalue) or isnan(minvalue): return self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) #if self.intband: # minvalue = int(minvalue) # maxvalue = int(maxvalue) if abs(maxvalue - minvalue) < 0.00001: #if maxvalue == maxvalue: self.calcHistPushButton.setEnabled(False) else: self.calcHistPushButton.setEnabled(True) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) self.minValueSpinBox.setMinimum(self.bandmin) def minmaxvalueEdFinished(self): minvalue = self.minValueSpinBox.value() maxvalue = self.maxValueSpinBox.value() if self.intband: minvalue = int(minvalue) maxvalue = int(maxvalue) self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) # Update the spin box for adding levels self.levelSpinBox.setMinimum(minvalue) self.levelSpinBox.setMaximum(maxvalue) if self.levelSpinBox.value() < minvalue: self.levelSpinBox.setValue(minvalue) if self.levelSpinBox.value() > maxvalue: self.levelSpinBox.setValue(maxvalue) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) # Adjust the levels: i = 0 while self.levelsListView.model().item(i): #for i in range(self.levelsListView.model().rowCount()): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) if value < minvalue: if i == 0: self.levelsListView.model().item(i).setText(str(minvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) elif value > maxvalue: if i == self.levelsListView.model().rowCount() - 1: self.levelsListView.model().item(i).setText(str(maxvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) else: i = i + 1 self.drawHistogram() def calculateHistogram(self): self.showInfo("Calculating histogram...") if self.inputlayer is None: return self.showInfo("Calculating histogram...") # Check if there is only one value myrange = (self.minValueSpinBox.value(), self.maxValueSpinBox.value()) self.inputextent = self.inputlayer.extent() self.inputrdp = self.inputlayer.dataProvider() width = self.inputlayer.width() height = self.inputlayer.height() if width == 0 or height == 0: self.showInfo("Image has zero width or height") return extwidth = self.inputextent.width() extheight = self.inputextent.height() # Read the raster block and get the maximum value rasterblock = self.inputrdp.block(1, self.inputextent, width, height) # Create a numpy array version of the image imageMat = np.zeros((height, width), dtype=np.float16) # This one takes a lot of time! for row in range(height): for column in range(width): imageMat[row, column] = rasterblock.value(row, column) self.showInfo("Image: " + str(height) + ", " + str(width) + " - " + str(imageMat[row, column])) self.histo = np.histogram(imageMat, self.histobins, myrange) #relevantpixels = imageMat[np.where(imageMat >= bandval)] minlevel = float(self.bandMinLabel.text()) relevantpixels = imageMat[np.where(imageMat >= minlevel)] #self.showInfo("Histogram: " + str(self.histo)) nanpercentage = 100.0 - 100.0 * len(relevantpixels) / (width * height) self.bandNANLabel.setText("{0:.1f}".format(nanpercentage)) #self.showInfo("Percentage NAN: " + str(100.0 - 100.0 * # len(relevantpixels) / (width * height))) #self.showInfo("First element: " + str(self.histo[0])) #self.showInfo("First element, first: " + str(self.histo[0][0])) #self.showInfo("First element, second: " + str(self.histo[0][1])) self.histMinValue.setText(str(self.minValueSpinBox.value())) self.histMaxValue.setText(str(self.maxValueSpinBox.value())) if self.intband: self.histMinValue.setText(str(int(self.minValueSpinBox.value()))) self.histMaxValue.setText(str(int(self.maxValueSpinBox.value()))) self.histogramAvailable = True self.drawHistogram() def drawHistogram(self): #if self.inputlayer is None: # return self.showInfo("Drawing histogram...") viewprect = QRectF(self.histoGraphicsView.viewport().rect()) self.histoGraphicsView.setSceneRect(viewprect) self.setupScene.clear() self.setupScene.update() histbottom = self.histoGraphicsView.sceneRect().bottom() histtop = self.histoGraphicsView.sceneRect().top() left = self.histoGraphicsView.sceneRect().left() + self.histopadding right = self.histoGraphicsView.sceneRect().right() - self.histopadding histheight = histbottom - histtop histwidth = right - left step = 1.0 * histwidth / self.histobins maxlength = histheight padding = 1 ll = QPoint(self.histopadding - 1, histheight - padding) start = QPointF(self.histoGraphicsView.mapToScene(ll)) # Check if there is only one value #myrange = (self.minValueSpinBox.value(),self.maxValueSpinBox.value()) if self.histogramAvailable: maxvalue = 0.0 for i in range(len(self.histo[0])): if self.histo[0][i] > maxvalue: maxvalue = self.histo[0][i] if maxvalue == 0: return self.maxBinNumber.setText(str(maxvalue)) # Create the histogram: #self.showInfo("maxvalue: " + str(maxvalue)) #self.showInfo("maxlength: " + str(maxlength)) #self.showInfo("step: " + str(step)) for i in range(self.histobins): binnumber = self.histo[0][i] if binnumber == 0: continue height = (1.0 * self.histo[0][i] / maxvalue * (maxlength - padding)) rectangle = QGraphicsRectItem(start.x() + step * i, start.y(), step, -height) rectangle.setPen(QPen(QColor(102, 102, 102))) rectangle.setBrush(QBrush(QColor(240, 240, 240))) self.setupScene.addItem(rectangle) #self.showInfo(str(i) + ": " + str(height)) #if self.levelsListView.model().rowCount() > 0: # Add lines for the levels minvalue = float(self.histMinValue.text()) maxvalue = float(self.histMaxValue.text()) datarange = maxvalue - minvalue if datarange == 0: return i = 0 while self.levelsListView.model().item(i): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) xvalue = start.x() + histwidth * (value - minvalue) / datarange line = QGraphicsLineItem(xvalue, 0, xvalue, histheight) if i == 0 or i == (self.levelsListView.model().rowCount() - 1): line.setPen(QPen(QColor(204, 0, 0))) else: line.setPen(QPen(QColor(0, 204, 0))) self.setupScene.addItem(line) i = i + 1 def suggestLevels(self): self.listModel.clear() self.showInfo("Suggesting levels") levels = self.levelsSpinBox.value() startvalue = self.minValueSpinBox.value() endvalue = self.maxValueSpinBox.value() increment = (endvalue - startvalue) / levels for i in range(levels + 1): value = startvalue + increment * i if self.intband: value = int(value) item = QStandardItem(str(value)) self.listModel.appendRow(item) self.drawHistogram() def addLevel(self): newvalue = self.levelSpinBox.value() if self.intband: newvalue = int(newvalue) for i in range(self.listModel.rowCount()): # Check if the value is already in the list if self.listModel.item(i).text() == str(newvalue): return else: # Maintain a sorted list of distances if (float(self.listModel.item(i).text()) > float(str(newvalue))): item = QStandardItem(str(newvalue)) self.listModel.insertRow(i, item) self.drawHistogram() return item = QStandardItem(str(newvalue)) self.listModel.appendRow(item) #if self.histogramAvailable: # addLevelsToHistogram() self.drawHistogram() def removeLevel(self): self.levelsListView.setUpdatesEnabled(False) indexes = self.levelsListView.selectedIndexes() indexes.sort() for i in range(len(indexes) - 1, -1, -1): self.listModel.removeRow(indexes[i].row()) self.levelsListView.setUpdatesEnabled(True) #if self.histogramAvailable: # removeLevelFromHistogram() self.drawHistogram() def layerlistchanged(self): self.layerlistchanging = True self.showInfo("Layer list changed") # Repopulate the input layer combo box # Save the currently selected input layer inputlayerid = self.inputlayerid self.inputRaster.clear() for alayer in self.iface.legendInterface().layers(): if alayer.type() == QgsMapLayer.RasterLayer: gdalmetadata = alayer.metadata() # Skip WMS layers WMSstring = 'Web Map Service' wmspos = gdalmetadata.find(WMSstring) if wmspos != -1: continue self.inputRaster.addItem(alayer.name(), alayer.id()) # Set the previous selection for i in range(self.inputRaster.count()): if self.inputRaster.itemData(i) == inputlayerid: self.inputRaster.setCurrentIndex(i) self.layerlistchanging = False #self.updateui() def updateui(self): """Do the necessary updates after a layer selection has been changed.""" #if self.layerlistchanged: # return #self.outputRaster.setText(self.inputRaster.currentText() + # '_' + 'thinned') layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) inputlayer = QgsProject.instance().mapLayer(layerId) if inputlayer is not None: pass else: pass def findGdalDatatype(self, shortdesc): gdaldatatype = None # // Unknown or unspecified type # GDT_Unknown = GDALDataType(C.GDT_Unknown) if shortdesc == 'Unknown': gdaldatatype = gdal.GDT_Unknown # // Eight bit unsigned integer # GDT_Byte = GDALDataType(C.GDT_Byte) elif shortdesc == 'Byte': gdaldatatype = gdal.GDT_Byte # // Sixteen bit unsigned integer # GDT_UInt16 = GDALDataType(C.GDT_UInt16) elif shortdesc == 'UInt16': gdaldatatype = gdal.GDT_UInt16 # // Sixteen bit signed integer # GDT_Int16 = GDALDataType(C.GDT_Int16) elif shortdesc == 'Int16': gdaldatatype = gdal.GDT_Int16 # // Thirty two bit unsigned integer # GDT_UInt32 = GDALDataType(C.GDT_UInt32) elif shortdesc == 'UInt32': gdaldatatype = gdal.GDT_UInt32 # // Thirty two bit signed integer # GDT_Int32 = GDALDataType(C.GDT_Int32) elif shortdesc == 'Int32': gdaldatatype = gdal.GDT_Int32 # // Thirty two bit floating point # GDT_Float32 = GDALDataType(C.GDT_Float32) elif shortdesc == 'Float32': gdaldatatype = gdal.GDT_Float32 # // Sixty four bit floating point # GDT_Float64 = GDALDataType(C.GDT_Float64) elif shortdesc == 'Float64': gdaldatatype = gdal.GDT_Float64 # // Complex Int16 # GDT_CInt16 = GDALDataType(C.GDT_CInt16) elif shortdesc == 'CInt16': gdaldatatype = gdal.CInt16 # // Complex Int32 # GDT_CInt32 = GDALDataType(C.GDT_CInt32) elif shortdesc == 'CInt32': gdaldatatype = gdal.CInt32 # // Complex Float32 # GDT_CFloat32 = GDALDataType(C.GDT_CFloat32) elif shortdesc == 'CFloat32': gdaldatatype = gdal.CFloat32 # // Complex Float64 # GDT_CFloat64 = GDALDataType(C.GDT_CFloat64) elif shortdesc == 'CFloat64': gdaldatatype = gdal.CFloat64 # // maximum type # + 1 # GDT_TypeCount = GDALDataType(C.GDT_TypeCount) elif shortdesc == 'TypeCount': gdaldatatype = gdal.TypeCount self.gdaldatatype = gdaldatatype def killWorker(self): """Kill the worker thread.""" if self.worker is not None: QgsMessageLog.logMessage(self.tr('Killing worker'), self.THINGREYSCALE, Qgis.Info) self.worker.kill() def showError(self, text): """Show an error.""" self.iface.messageBar().pushMessage(self.tr('Error'), text, level=QgsMessageBar.CRITICAL, duration=3) QgsMessageLog.logMessage('Error: ' + text, self.THINGREYSCALE, QgsMessageLog.CRITICAL) def showWarning(self, text): """Show a warning.""" self.iface.messageBar().pushMessage(self.tr('Warning'), text, level=QgsMessageBar.WARNING, duration=2) QgsMessageLog.logMessage('Warning: ' + text, self.THINGREYSCALE, QgsMessageLog.WARNING) def showInfo(self, text): """Show info.""" self.iface.messageBar().pushMessage(self.tr('Info'), text, level=Qgis.Info, duration=2) QgsMessageLog.logMessage('Info: ' + text, self.THINGREYSCALE, Qgis.Info) # def help(self): # #QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/build/html/index.html")) # QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/index.html")) # #showPluginHelp() def tr(self, message): """Get the translation for a string using Qt translation API. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ return QCoreApplication.translate('ThinGreyScaleDialog', message) def browse(self): settings = QSettings() key = '/UI/lastShapefileDir' outDir = settings.value(key) home = outDir #outDir = expanduser("~") #filter = (self.DEFAULTPROVIDER + " (*" + # self.DEFAULTEXTENSION + ");;All files (*)") filter = (self.DEFAULTPROVIDER + " (*" + self.DEFAULTEXTENSION + self.EXTRAEXTENSION + ")") #if (self.gdalprovider != self.DEFAULTPROVIDER and # (self.canCreateCopy or # self.canCreate)): # filter = (self.gdalprovider + " (*" + self.gdalext + # ");;" + filter) outFilePath = QFileDialog.getSaveFileName(self, 'Specify file name for skeleton', outDir, filter) outFilePath = unicode(outFilePath) if outFilePath: root, ext = splitext(outFilePath) if ext.lower() != '.tif' and ext.lower() != '.tiff': outFilePath = '%s.tif' % outFilePath outDir = dirname(outFilePath) settings.setValue(key, outDir) # (self.canCreateCopy or self.canCreate): # fileName = splitext(str(fileName))[0]+self.gdalext self.outputRaster.setText(outFilePath) # Overriding def resizeEvent(self, event): #self.showInfo("resizeEvent") self.calculateHistogram() def help(self): #QDesktopServices.openUrl(QUrl.fromLocalFile( # self.plugin_dir + "/help/html/index.html")) showPluginHelp(None, "help/html/index") # Implement the accept method to avoid exiting the dialog when # starting the work def accept(self): """Accept override.""" pass # Implement the reject method to have the possibility to avoid # exiting the dialog when cancelling def reject(self): """Reject override.""" # exit the dialog QDialog.reject(self)
class StripChart: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'StripChart_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'Spectral data') self.toolbar = self.iface.addToolBar(u'Stripchart') self.toolbar.setObjectName(u'Stripchart') #QgsMessageLog.logMessage(message, tag, level)("** INITIALIZING StripChart") self.view = MouseReadGraphicsView(self.iface) self.view.layer = None self.pluginIsActive = False self.view.parent = self # self.dockwidget = None # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('Stripchart', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToVectorMenu(self.menu, action) self.actions.append(action) return action def selectedlayer(self): if self.dlg.qgLayer.currentLayer() == None: return self.dlg.qgField.setLayer(self.dlg.qgLayer.currentLayer()) self.dlg.qgField.setField(None) self.clearscene() def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/stripchart/icon.png' self.add_action(icon_path, text=self.tr(u'Stripchart'), callback=self.run, parent=self.iface.mainWindow()) self.dlg = StripChartDockWidget(self.iface.mainWindow()) self.view.setParent(self.dlg) self.dlg.vlMain.addWidget(self.view) self.scene = QGraphicsScene() self.view.setScene(self.scene) self.scene.setSceneRect(0, 0, 300, 2000) self.dlg.qgLayer.setFilters(QgsMapLayerProxyModel.VectorLayer) self.dlg.qgLayer.setLayer(self.iface.activeLayer()) self.dlg.qgField.setAllowEmptyFieldName(True) self.dlg.qgField.setLayer(self.dlg.qgLayer.currentLayer()) self.dlg.qgField.setFilters(QgsFieldProxyModel.Numeric) self.dlg.qgLayer.layerChanged.connect(self.selectedlayer) self.iface.mapCanvas().selectionChanged.connect(self.markselected) self.dlg.qgField.currentIndexChanged['QString'].connect( self.stripchart) #-------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" # disconnects self.dlg.closingPlugin.disconnect(self.onClosePlugin) self.pluginIsActive = False def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" #print "** UNLOAD StripChart" for action in self.actions: self.iface.removePluginVectorMenu(self.tr(u'Spectral data'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar #-------------------------------------------------------------------------- def clearscene(self): self.scene.clear() self.view.selectlines = [] self.scene.values = [] self.view.ids = [] # Keeps the ids . def stripchart(self): """ Draws a stripchart based on a dataset """ self.view.layer = self.dlg.qgLayer.currentLayer() if self.view.layer == None or self.dlg.isHidden(): return if self.view.layer.featureCount() == 0: self.iface.messageBar().pushMessage( "Stripchart", "No data in table, cannot draw stripchart", level=Qgis.Critical, duration=3) # Info, Warning, Critical, Success return QgsMessageLog.logMessage("Stripchart starting", "Messages", 0) self.clearscene() idfields = self.view.layer.dataProvider().pkAttributeIndexes( ) # These are the fields that build up the primary key if len(idfields) == 0: try: self.view.idfield = self.view.layer.fields()[0].name() self.iface.messageBar().pushMessage( "Warning", "No primary key for {}, sorting on {}, - selection may not be possible" .format(self.view.layer.name(), self.view.idfield), level=Qgis.Warning) # Info, Warning, Critical, Success except IndexError as e: # Probably undefined layer, just return # This happens some times when exiting QGIS QgsMessageLog.logMessage( "Could not draw stripchart - IndexError", "Messages", Qgis.Warning) return else: #idfield=idfields[0] self.view.idfield = self.view.layer.fields()[idfields[0]].name() self.iface.messageBar().pushMessage( "Info", "Sorting on {}".format(self.view.idfield), level=Qgis.Info, duration=3) # Info, Warning, Critical, Success fieldname = self.dlg.qgField.currentText() if fieldname == '' or fieldname is None: return request = QgsFeatureRequest().addOrderBy(self.view.idfield).setFlags( QgsFeatureRequest.NoGeometry).setSubsetOfAttributes( [self.view.idfield, fieldname], self.view.layer.fields()) iter = self.view.layer.getFeatures(request) for feature in iter: if isinstance(feature[fieldname], list): self.iface.messageBar().pushMessage( "Error", "Invalid field type : {}".format(fieldname), level=Qgis.Warning, duration=3) # Info, Warning, Critical, Success return self.scene.values.append(feature[fieldname]) self.view.ids.append(feature[self.view.idfield]) # QgsMessageLog.logMessage("Added id {}".format(feature[self.view.idfield]), "Messages", Qgis.Info) self.scene.setSceneRect(0, 0, self.view.width, len(self.scene.values)) airfact = 0.02 maxval = max(self.scene.values) minval = min(self.scene.values) print(maxval, minval) if maxval == None: # Field with only "None" values return air = (maxval - minval) * airfact if maxval > 0: maxval += air else: maxval -= air if minval > 0: minval -= air else: minval += air if maxval - minval == 0: scale = self.view.width / maxval # Could just as well return since this will plot a straight line... else: scale = self.view.width / (maxval - minval) n = 0 for v in self.scene.values: v -= minval self.scene.addLine(0, n, v * scale, n) n += 1 self.markselected( ) # In case something is already selected when the layer is plotted def run(self): """Run method that loads and starts the plugin""" self.init = True if not self.pluginIsActive: self.pluginIsActive = True # connect to provide cleanup on closing of dockwidget self.dlg.closingPlugin.connect(self.onClosePlugin) # show the dockwidget self.iface.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self.dlg) self.dlg.show() def markselected(self): """Marks in the stripchart which elements that are selected""" try: if self.view.layer == None: return QgsMessageLog.logMessage("Going to look into selection", "Messages", Qgis.Info) sels = self.view.layer.selectedFeatures( ) # The selected features in the active (from this plugin's point of view) layer n = len(sels) QgsMessageLog.logMessage("Selected {}".format(n), "Messages", Qgis.Info) self.view.clearselection() QgsMessageLog.logMessage("Selection cleared", "Messages", Qgis.Info) if n > 0: self.view.markselection(sels) except: try: self.iface.messageBar().pushMessage( "Stripchart", "Error during selection from {}".format( self.view.layer.name()), level=Qgis.Warning, duration=3) # Info, Warning, Critical, Success except RuntimeError: print("Error in markselected:") print(e) except Exception as e: self.iface.messageBar().pushMessage( "Stripchart", "Error during selection ", level=Qgis.Warning, duration=3) # Info, Warning, Critical, Success print(e) print(self.view.layer.name())
class ThinGreyscaleDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" self.iface = iface self.plugin_dir = dirname(__file__) self.THINGREYSCALE = self.tr('ThinGreyscale') self.BROWSE = self.tr('Browse') self.CANCEL = self.tr('Cancel') self.CLOSE = self.tr('Close') self.HELP = self.tr('Help') self.OK = self.tr('OK') self.DEFAULTPROVIDER = 'GTiff' self.DEFAULTEXTENSION = '.tif' self.EXTRAEXTENSION = ' *.tiff' super(ThinGreyscaleDialog, self).__init__(parent) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self.showInfo("Connecting UI components") okButton = self.button_box.button(QDialogButtonBox.Ok) okButton.setText(self.OK) cancelButton = self.button_box.button(QDialogButtonBox.Cancel) cancelButton.setText(self.CANCEL) cancelButton.setEnabled(False) closeButton = self.button_box.button(QDialogButtonBox.Close) closeButton.setText(self.CLOSE) browseButton = self.browseButton browseButton.setText(self.BROWSE) self.calcHistPushButton.setEnabled(False) self.listModel = QStandardItemModel(self.levelsListView) self.levelsListView.setModel(self.listModel) self.levelsListView.sizeHintForColumn(20) #self.levelValuesCheckBox.setEnabled(False) # Help button helpButton = self.helpButton helpButton.setText(self.HELP) # Connect signals self.showInfo("Connecting signals") okButton.clicked.connect(self.startWorker) cancelButton.clicked.connect(self.killWorker) closeButton.clicked.connect(self.reject) helpButton.clicked.connect(self.help) browseButton.clicked.connect(self.browse) inpIndexCh = self.inputRaster.currentIndexChanged['QString'] inpIndexCh.connect(self.layerchanged) bandCh = self.bandComboBox.currentIndexChanged['QString'] bandCh.connect(self.bandChanged) #self.iface.legendInterface().itemAdded.connect( # self.layerlistchanged) #self.iface.legendInterface().itemRemoved.connect( # self.layerlistchanged) #QObject.disconnect(self.button_box, SIGNAL("rejected()"), self.reject) self.button_box.rejected.disconnect(self.reject) calchistPr = self.calcHistPushButton.clicked calchistPr.connect(self.calculateHistogram) sugglevPr = self.suggestlevelsPushButton.clicked sugglevPr.connect(self.suggestLevels) addlevPr = self.addlevelPushButton.clicked addlevPr.connect(self.addLevel) dellevPr = self.deletelevelsPushButton.clicked dellevPr.connect(self.removeLevel) maxvalCh = self.maxValueSpinBox.valueChanged maxvalCh.connect(self.minmaxvalueChanged) maxvalFi = self.maxValueSpinBox.editingFinished maxvalFi.connect(self.minmaxvalueEdFinished) minvalCh = self.minValueSpinBox.valueChanged minvalCh.connect(self.minmaxvalueChanged) minvalFi = self.minValueSpinBox.editingFinished minvalFi.connect(self.minmaxvalueEdFinished) # Set instance variables #self.mem_layer = None self.worker = None self.inputlayerid = None self.inputlayer = None self.layerlistchanging = False self.minvalue = 1 self.inputrasterprovider = None self.histobins = 50 self.setupScene = QGraphicsScene(self) self.histoGraphicsView.setScene(self.setupScene) # Is the layer band of an integer type self.intband = False self.histogramAvailable = False self.histo = None self.histopadding = 1 def startWorker(self): """Initialises and starts the worker thread.""" try: layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) inputlayer = QgsProject.instance().mapLayer(layerId) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) if inputlayer is None: self.showError(self.tr('No input layer defined')) return # create a reference to the layer that is being processed # (for use when creating the resulting raster layer) self.thinninglayer = inputlayer self.levels = [] #self.levelsListView.selectAll() #selected = self.levelsListView.selectedIndexes() if self.levelsListView.model().rowCount() == 0: self.showInfo("Levels must be specified!") return for i in range(self.levelsListView.model().rowCount()): levelstring = self.levelsListView.model().item(i).text() #for i in selected: # levelstring = self.levelsListView.model().itemData(i)[0] if self.intband: self.levels.append(int(levelstring)) else: self.levels.append(float(levelstring)) #self.levelsListView.clearSelection() # create a new worker instance worker = Worker(inputlayer, self.levels, self.intband) # configure the QgsMessageBar msgBar = self.iface.messageBar().createMessage( self.tr('Skeletonising'), '') self.aprogressBar = QProgressBar() self.aprogressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) acancelButton = QPushButton() acancelButton.setText(self.CANCEL) acancelButton.clicked.connect(self.killWorker) msgBar.layout().addWidget(self.aprogressBar) msgBar.layout().addWidget(acancelButton) # Has to be popped after the thread has finished (in # workerFinished). self.iface.messageBar().pushWidget(msgBar, Qgis.Info) self.messageBar = msgBar # start the worker in a new thread thread = QThread(self) worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) worker.status.connect(self.workerInfo) worker.progress.connect(self.progressBar.setValue) worker.progress.connect(self.aprogressBar.setValue) worker.iterprogress.connect(self.iterProgressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.button(QDialogButtonBox.Close).setEnabled(False) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(True) except: import traceback self.showError(traceback.format_exc()) else: pass def workerFinished(self, ok, ret): """Handles the output from the worker and cleans up after the worker has finished.""" # clean up the worker and thread self.showInfo("Handling the result") self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() # remove widget from message bar (pop) self.iface.messageBar().popWidget(self.messageBar) if ok and ret is not None: #self.showInfo("Ret: "+str(ret[10,])) # Transformation: self.minx = self.thinninglayer.extent().xMinimum() self.maxx = self.thinninglayer.extent().xMaximum() self.miny = self.thinninglayer.extent().yMinimum() self.maxy = self.thinninglayer.extent().yMaximum() self.rows = self.thinninglayer.height() self.cols = self.thinninglayer.width() self.xres = (self.maxx - self.minx) / float(self.cols) self.yres = (self.maxy - self.miny) / float(self.rows) geotransform = (self.minx, self.xres, 0, self.maxy, 0, -self.yres) try: format = self.DEFAULTPROVIDER driver = gdal.GetDriverByName(format) NOVALUE = 0 metadata = driver.GetMetadata() fileName = self.outputRaster.text() if self.outputRaster.text() == "": self.showInfo("No output file specified, " + "creating a temporary file") # Get a temporary file fileName = mktemp(prefix='greyskel', suffix=self.DEFAULTEXTENSION) fileInfo = QFileInfo(fileName) filepath = fileInfo.absolutePath() baseName = fileInfo.baseName() suffix = fileInfo.suffix() thisfilename = filepath + baseName + '.' + suffix thisfilename = fileName self.showInfo("File name: " + thisfilename) gdaldatatype = gdal.GDT_Byte skelmatrix = None if self.levelValuesCheckBox.isChecked(): # Transform the pixel values back to the original # level values my_dict = {} # Add zero to handle the "empty" pixels my_dict[0] = 0 for i in range(len(self.levels)): my_dict[i + 1] = self.levels[i] skelmatrix = np.vectorize(my_dict.__getitem__, otypes=[np.float])(ret) gdaldatatype = gdal.GDT_Int32 if not self.intband: gdaldatatype = gdal.GDT_Float32 else: skelmatrix = ret outDataset = driver.Create(thisfilename, self.cols, self.rows, 1, gdaldatatype) if self.thinninglayer.dataProvider().crs() is not None: srs = self.thinninglayer.dataProvider().crs() outDataset.SetProjection(srs.toWkt().encode( 'ascii', 'ignore')) skeletonband = outDataset.GetRasterBand(1) skeletonband.WriteArray(skelmatrix) skeletonband.SetNoDataValue(NOVALUE) #stats = skeletonband.GetStatistics(False, True) #skeletonband.SetStatistics(stats[0], stats[1], # stats[2], stats[3]) outDataset.SetGeoTransform(geotransform) outDataset = None # To close the file # report the result rlayer = QgsRasterLayer(thisfilename, baseName) self.layerlistchanging = True #QgsMapLayerRegistry.instance().addMapLayer(rlayer) QgsProject.instance().addMapLayer(rlayer) self.layerlistchanging = False except: import traceback self.showError("Can't write the skeleton file: %s" % self.outputRaster.text() + ' - ' + traceback.format_exc()) okb = self.button_box.button(QDialogButtonBox.Ok) okb.setEnabled(True) closb = self.button_box.button(QDialogButtonBox.Close) closb.setEnabled(True) cancb = self.button_box.button(QDialogButtonBox.Cancel) cancb.setEnabled(False) return QgsMessageLog.logMessage(self.tr('ThinGreyscale finished'), self.THINGREYSCALE, Qgis.Info) else: # notify the user that something went wrong if not ok: self.showError(self.tr('Aborted') + '!') else: self.showError(self.tr('No skeleton created') + '!') self.progressBar.setValue(0.0) #self.aprogressBar.setValue(0.0) self.iterProgressBar.setValue(0.0) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Close).setEnabled(True) self.button_box.button(QDialogButtonBox.Cancel).setEnabled(False) def workerError(self, exception_string): """Report an error from the worker.""" #QgsMessageLog.logMessage(self.tr('Worker failed - exception') + # ': ' + str(exception_string), # self.THINGREYSCALE, # QgsMessageLog.CRITICAL) self.showError(exception_string) def workerInfo(self, message_string): """Report an info message from the worker.""" QgsMessageLog.logMessage( self.tr('Worker') + ': ' + message_string, self.THINGREYSCALE, Qgis.Info) def layerchanged(self, number=0): """Do the necessary updates after a layer selection has been changed.""" self.showInfo("Layer changed") # If the layer list is being updated, don't do anything if self.layerlistchanging: return layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) self.inputlayerid = layerId #self.inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) self.inputlayer = QgsProject.instance().mapLayer(layerId) if self.inputlayer is not None: self.inputrasterprovider = self.inputlayer.dataProvider() self.bandComboBox.clear() bandcount = self.inputlayer.bandCount() #self.showInfo("Layer bandcount: "+str(bandcount)) for i in range(bandcount): self.bandComboBox.addItem(self.inputlayer.bandName(i + 1), i) #self.showInfo("Band " + str(i) + ": " + # self.inputlayer.bandName(i+1)) # Check if the driver supports Create() or CreateCopy() #gdalmetadata = self.inputlayer.metadata() #self.showInfo("Layer metadata: " + # str(gdalmetadata.encode('utf-8'))) #provstring = '<p>GDAL provider</p>\n' #providerpos = gdalmetadata.find(provstring) #brpos = gdalmetadata.find('<br>', providerpos + len(provstring)) #self.gdalprovider = gdalmetadata[int(providerpos + # len(provstring)):int(brpos)] #self.showInfo('GDAL provider: '+self.gdalprovider) #drivername = self.gdalprovider.encode('ascii', 'ignore') #theDriver = gdal.GetDriverByName(drivername) #if theDriver is None: # self.showInfo("Unable to get the raster driver") #else: #data theMetadata = theDriver.GetMetadata() #self.showInfo("Driver metadata: "+str(theMetadata)) #if ((gdal.DCAP_CREATE in theMetadata) and # theMetadata[gdal.DCAP_CREATE] == 'YES'): # self.canCreate = True #if (theMetadata.has_key(gdal.DCAP_CREATECOPY) and #if ((gdal.DCAP_CREATECOPY in theMetadata) and # theMetadata[gdal.DCAP_CREATECOPY] == 'YES'): # self.canCreateCopy = True #self.showInfo('raster provider type: ' + # str(self.inputlayer.providerType())) # Determine the file suffix #self.gdalext = "" #if gdal.DMD_EXTENSION in theMetadata: # self.gdalext = "." + theMetadata[gdal.DMD_EXTENSION] #else: # self.showInfo("No extension available in GDAL metadata") # by parsing the layer metadata looking for # "Dataset Description" #descstring = 'Dataset Description</p>\n<p>' #descpos = gdalmetadata.find(descstring) #ppos = gdalmetadata.find('</p>',descpos+len(descstring)) #filename = gdalmetadata[descpos+len(descstring):ppos] #self.gdalext = splitext(filename)[1] #self.showInfo('GDAL extension: '+self.gdalext) # Determine the datatype #datatypestring = 'Data Type</p>\n<p>' #datatypepos = gdalmetadata.find(datatypestring) #ppos = gdalmetadata.find('</p>', # datatypepos + len(datatypestring)) #datatypedesc = gdalmetadata[datatypepos + # len(datatypestring):ppos] #shortdesc = datatypedesc.split()[0] #self.showInfo('GDAL data type: GDT_'+shortdesc) # Call the findGdalDatatype function #self.findGdalDatatype(shortdesc) # self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) self.calcHistPushButton.setEnabled(True) self.suggestlevelsPushButton.setEnabled(True) def bandChanged(self): band = self.bandComboBox.currentIndex() + 1 self.showInfo("Band changed: " + str(band)) statistics = self.inputrasterprovider.bandStatistics(band) #self.showInfo("Band statistics: " + str(statistics.minimumValue) + # " - " + str(statistics.maximumValue) + # " - " + str(statistics.mean)) self.bandmin = statistics.minimumValue self.bandmax = statistics.maximumValue dt = self.inputrasterprovider.dataType(band) # Integer data type if (dt == Qgis.Byte or dt == Qgis.UInt16 or dt == Qgis.Int16 or dt == Qgis.UInt32 or dt == Qgis.Int32): self.intband = True self.minValueSpinBox.setDecimals(0) self.maxValueSpinBox.setDecimals(0) self.levelSpinBox.setDecimals(0) self.bandMinLabel.setText(str(int(statistics.minimumValue))) self.bandMaxLabel.setText(str(int(statistics.maximumValue))) else: self.intband = False self.minValueSpinBox.setDecimals(5) self.maxValueSpinBox.setDecimals(5) self.levelSpinBox.setDecimals(5) minlabtext = "{0:.5f}".format(statistics.minimumValue) self.bandMinLabel.setText(minlabtext) maxlabtext = "{0:.5f}".format(statistics.maximumValue) self.bandMaxLabel.setText(maxlabtext) #self.minValueSpinBox.setMinimum(statistics.minimumValue) self.maxValueSpinBox.setMinimum(statistics.minimumValue) #self.minValueSpinBox.setMaximum(statistics.maximumValue) self.maxValueSpinBox.setMaximum(statistics.maximumValue) #self.minValueSpinBox.setValue(statistics.minimumValue) if not (statistics.statsGathered & statistics.Mean): bandmean = (statistics.minimumValue + statistics.maximumValue) / 2 else: #self.showInfo("statsgathered: " + str(statistics.statsGathered)) bandmean = statistics.mean if self.intband: self.minValueSpinBox.setValue(int(ceil(bandmean))) else: self.minValueSpinBox.setValue(bandmean) self.maxValueSpinBox.setValue(statistics.maximumValue) self.histMinValue.setText(str(statistics.minimumValue)) self.histMaxValue.setText(str(statistics.maximumValue)) self.levelSpinBox.setMinimum(statistics.minimumValue) self.levelSpinBox.setMaximum(statistics.maximumValue) self.histogramAvailable = False #if self.inputrasterprovider.hasStatistics(band): #if statistics.statsGathered: #histogram = statistics.histogramVector #self.showInfo("Histogram: " + str(histogram)) #range = min to max #np.histogram(band, 50, range) def minmaxvalueChanged(self): #if self.minValueSpinBox is None: # return minvalue = self.minValueSpinBox.value() #if minvalue is None: # return #if self.maxValueSpinBox is None: # return maxvalue = self.maxValueSpinBox.value() #if maxvalue is None: # return if isnan(maxvalue) or isnan(minvalue): return self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) #if self.intband: # minvalue = int(minvalue) # maxvalue = int(maxvalue) if abs(maxvalue - minvalue) < 0.00001: #if maxvalue == maxvalue: self.calcHistPushButton.setEnabled(False) else: self.calcHistPushButton.setEnabled(True) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) self.minValueSpinBox.setMinimum(self.bandmin) def minmaxvalueEdFinished(self): minvalue = self.minValueSpinBox.value() maxvalue = self.maxValueSpinBox.value() if self.intband: minvalue = int(minvalue) maxvalue = int(maxvalue) self.showInfo("minvalue: " + str(minvalue) + " Maxvalue: " + str(maxvalue)) # Update the spin box for adding levels self.levelSpinBox.setMinimum(minvalue) self.levelSpinBox.setMaximum(maxvalue) if self.levelSpinBox.value() < minvalue: self.levelSpinBox.setValue(minvalue) if self.levelSpinBox.value() > maxvalue: self.levelSpinBox.setValue(maxvalue) # Update the min and max value spinboxes self.minValueSpinBox.setMaximum(maxvalue) self.maxValueSpinBox.setMinimum(minvalue) # Adjust the levels: i = 0 while self.levelsListView.model().item(i): #for i in range(self.levelsListView.model().rowCount()): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) if value < minvalue: if i == 0: self.levelsListView.model().item(i).setText(str(minvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) elif value > maxvalue: if i == self.levelsListView.model().rowCount() - 1: self.levelsListView.model().item(i).setText(str(maxvalue)) i = i + 1 else: self.levelsListView.model().removeRow(i) else: i = i + 1 self.drawHistogram() def calculateHistogram(self): self.showInfo("Calculating histogram...") if self.inputlayer is None: return self.showInfo("Calculating histogram...") # Check if there is only one value myrange = (self.minValueSpinBox.value(), self.maxValueSpinBox.value()) self.inputextent = self.inputlayer.extent() self.inputrdp = self.inputlayer.dataProvider() width = self.inputlayer.width() height = self.inputlayer.height() if width == 0 or height == 0: self.showInfo("Image has zero width or height") return extwidth = self.inputextent.width() extheight = self.inputextent.height() # Read the raster block and get the maximum value rasterblock = self.inputrdp.block(1, self.inputextent, width, height) # Create a numpy array version of the image imageMat = np.zeros((height, width), dtype=np.float16) # This one takes a lot of time! for row in range(height): for column in range(width): imageMat[row, column] = rasterblock.value(row, column) self.showInfo("Image: " + str(height) + ", " + str(width) + " - " + str(imageMat[row, column])) self.histo = np.histogram(imageMat, self.histobins, myrange) #relevantpixels = imageMat[np.where(imageMat >= bandval)] minlevel = float(self.bandMinLabel.text()) relevantpixels = imageMat[np.where(imageMat >= minlevel)] #self.showInfo("Histogram: " + str(self.histo)) nanpercentage = 100.0 - 100.0 * len(relevantpixels) / (width * height) self.bandNANLabel.setText("{0:.1f}".format(nanpercentage)) #self.showInfo("Percentage NAN: " + str(100.0 - 100.0 * # len(relevantpixels) / (width * height))) #self.showInfo("First element: " + str(self.histo[0])) #self.showInfo("First element, first: " + str(self.histo[0][0])) #self.showInfo("First element, second: " + str(self.histo[0][1])) self.histMinValue.setText(str(self.minValueSpinBox.value())) self.histMaxValue.setText(str(self.maxValueSpinBox.value())) if self.intband: self.histMinValue.setText(str(int(self.minValueSpinBox.value()))) self.histMaxValue.setText(str(int(self.maxValueSpinBox.value()))) self.histogramAvailable = True self.drawHistogram() def drawHistogram(self): #if self.inputlayer is None: # return self.showInfo("Drawing histogram...") viewprect = QRectF(self.histoGraphicsView.viewport().rect()) self.histoGraphicsView.setSceneRect(viewprect) self.setupScene.clear() self.setupScene.update() histbottom = self.histoGraphicsView.sceneRect().bottom() histtop = self.histoGraphicsView.sceneRect().top() left = self.histoGraphicsView.sceneRect().left() + self.histopadding right = self.histoGraphicsView.sceneRect().right() - self.histopadding histheight = histbottom - histtop histwidth = right - left step = 1.0 * histwidth / self.histobins maxlength = histheight padding = 1 ll = QPoint(self.histopadding - 1, histheight - padding) start = QPointF(self.histoGraphicsView.mapToScene(ll)) # Check if there is only one value #myrange = (self.minValueSpinBox.value(),self.maxValueSpinBox.value()) if self.histogramAvailable: maxvalue = 0.0 for i in range(len(self.histo[0])): if self.histo[0][i] > maxvalue: maxvalue = self.histo[0][i] if maxvalue == 0: return self.maxBinNumber.setText(str(maxvalue)) # Create the histogram: #self.showInfo("maxvalue: " + str(maxvalue)) #self.showInfo("maxlength: " + str(maxlength)) #self.showInfo("step: " + str(step)) for i in range(self.histobins): binnumber = self.histo[0][i] if binnumber == 0: continue height = (1.0 * self.histo[0][i] / maxvalue * (maxlength - padding)) rectangle = QGraphicsRectItem(start.x() + step * i, start.y(), step, -height) rectangle.setPen(QPen(QColor(102, 102, 102))) rectangle.setBrush(QBrush(QColor(240, 240, 240))) self.setupScene.addItem(rectangle) #self.showInfo(str(i) + ": " + str(height)) #if self.levelsListView.model().rowCount() > 0: # Add lines for the levels minvalue = float(self.histMinValue.text()) maxvalue = float(self.histMaxValue.text()) datarange = maxvalue - minvalue if datarange == 0: return i = 0 while self.levelsListView.model().item(i): #self.showInfo("Element: " + # str(self.levelsListView.model().item(i).text())) #continue value = float(self.levelsListView.model().item(i).text()) xvalue = start.x() + histwidth * (value - minvalue) / datarange line = QGraphicsLineItem(xvalue, 0, xvalue, histheight) if i == 0 or i == (self.levelsListView.model().rowCount() - 1): line.setPen(QPen(QColor(204, 0, 0))) else: line.setPen(QPen(QColor(0, 204, 0))) self.setupScene.addItem(line) i = i + 1 def suggestLevels(self): self.listModel.clear() self.showInfo("Suggesting levels") levels = self.levelsSpinBox.value() startvalue = self.minValueSpinBox.value() endvalue = self.maxValueSpinBox.value() increment = (endvalue - startvalue) / levels for i in range(levels + 1): value = startvalue + increment * i if self.intband: value = int(value) item = QStandardItem(str(value)) self.listModel.appendRow(item) self.drawHistogram() def addLevel(self): newvalue = self.levelSpinBox.value() if self.intband: newvalue = int(newvalue) for i in range(self.listModel.rowCount()): # Check if the value is already in the list if self.listModel.item(i).text() == str(newvalue): return else: # Maintain a sorted list of distances if (float(self.listModel.item(i).text()) > float( str(newvalue))): item = QStandardItem(str(newvalue)) self.listModel.insertRow(i, item) self.drawHistogram() return item = QStandardItem(str(newvalue)) self.listModel.appendRow(item) #if self.histogramAvailable: # addLevelsToHistogram() self.drawHistogram() def removeLevel(self): self.levelsListView.setUpdatesEnabled(False) indexes = self.levelsListView.selectedIndexes() indexes.sort() for i in range(len(indexes) - 1, -1, -1): self.listModel.removeRow(indexes[i].row()) self.levelsListView.setUpdatesEnabled(True) #if self.histogramAvailable: # removeLevelFromHistogram() self.drawHistogram() def layerlistchanged(self): self.layerlistchanging = True self.showInfo("Layer list changed") # Repopulate the input layer combo box # Save the currently selected input layer inputlayerid = self.inputlayerid self.inputRaster.clear() for alayer in self.iface.legendInterface().layers(): if alayer.type() == QgsMapLayer.RasterLayer: gdalmetadata = alayer.metadata() # Skip WMS layers WMSstring = 'Web Map Service' wmspos = gdalmetadata.find(WMSstring) if wmspos != -1: continue self.inputRaster.addItem(alayer.name(), alayer.id()) # Set the previous selection for i in range(self.inputRaster.count()): if self.inputRaster.itemData(i) == inputlayerid: self.inputRaster.setCurrentIndex(i) self.layerlistchanging = False #self.updateui() def updateui(self): """Do the necessary updates after a layer selection has been changed.""" #if self.layerlistchanged: # return #self.outputRaster.setText(self.inputRaster.currentText() + # '_' + 'thinned') layerindex = self.inputRaster.currentIndex() layerId = self.inputRaster.itemData(layerindex) #inputlayer = QgsMapLayerRegistry.instance().mapLayer(layerId) inputlayer = QgsProject.instance().mapLayer(layerId) if inputlayer is not None: pass else: pass def findGdalDatatype(self, shortdesc): gdaldatatype = None # // Unknown or unspecified type # GDT_Unknown = GDALDataType(C.GDT_Unknown) if shortdesc == 'Unknown': gdaldatatype = gdal.GDT_Unknown # // Eight bit unsigned integer # GDT_Byte = GDALDataType(C.GDT_Byte) elif shortdesc == 'Byte': gdaldatatype = gdal.GDT_Byte # // Sixteen bit unsigned integer # GDT_UInt16 = GDALDataType(C.GDT_UInt16) elif shortdesc == 'UInt16': gdaldatatype = gdal.GDT_UInt16 # // Sixteen bit signed integer # GDT_Int16 = GDALDataType(C.GDT_Int16) elif shortdesc == 'Int16': gdaldatatype = gdal.GDT_Int16 # // Thirty two bit unsigned integer # GDT_UInt32 = GDALDataType(C.GDT_UInt32) elif shortdesc == 'UInt32': gdaldatatype = gdal.GDT_UInt32 # // Thirty two bit signed integer # GDT_Int32 = GDALDataType(C.GDT_Int32) elif shortdesc == 'Int32': gdaldatatype = gdal.GDT_Int32 # // Thirty two bit floating point # GDT_Float32 = GDALDataType(C.GDT_Float32) elif shortdesc == 'Float32': gdaldatatype = gdal.GDT_Float32 # // Sixty four bit floating point # GDT_Float64 = GDALDataType(C.GDT_Float64) elif shortdesc == 'Float64': gdaldatatype = gdal.GDT_Float64 # // Complex Int16 # GDT_CInt16 = GDALDataType(C.GDT_CInt16) elif shortdesc == 'CInt16': gdaldatatype = gdal.CInt16 # // Complex Int32 # GDT_CInt32 = GDALDataType(C.GDT_CInt32) elif shortdesc == 'CInt32': gdaldatatype = gdal.CInt32 # // Complex Float32 # GDT_CFloat32 = GDALDataType(C.GDT_CFloat32) elif shortdesc == 'CFloat32': gdaldatatype = gdal.CFloat32 # // Complex Float64 # GDT_CFloat64 = GDALDataType(C.GDT_CFloat64) elif shortdesc == 'CFloat64': gdaldatatype = gdal.CFloat64 # // maximum type # + 1 # GDT_TypeCount = GDALDataType(C.GDT_TypeCount) elif shortdesc == 'TypeCount': gdaldatatype = gdal.TypeCount self.gdaldatatype = gdaldatatype def killWorker(self): """Kill the worker thread.""" if self.worker is not None: QgsMessageLog.logMessage(self.tr('Killing worker'), self.THINGREYSCALE, Qgis.Info) self.worker.kill() def showError(self, text): """Show an error.""" self.iface.messageBar().pushMessage(self.tr('Error'), text, level=QgsMessageBar.CRITICAL, duration=3) QgsMessageLog.logMessage('Error: ' + text, self.THINGREYSCALE, QgsMessageLog.CRITICAL) def showWarning(self, text): """Show a warning.""" self.iface.messageBar().pushMessage(self.tr('Warning'), text, level=QgsMessageBar.WARNING, duration=2) QgsMessageLog.logMessage('Warning: ' + text, self.THINGREYSCALE, QgsMessageLog.WARNING) def showInfo(self, text): """Show info.""" self.iface.messageBar().pushMessage(self.tr('Info'), text, level=Qgis.Info, duration=2) QgsMessageLog.logMessage('Info: ' + text, self.THINGREYSCALE, Qgis.Info) # def help(self): # #QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/build/html/index.html")) # QDesktopServices.openUrl(QUrl.fromLocalFile(self.plugin_dir + # "/help/index.html")) # #showPluginHelp() def tr(self, message): """Get the translation for a string using Qt translation API. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ return QCoreApplication.translate('ThinGreyScaleDialog', message) def browse(self): settings = QSettings() key = '/UI/lastShapefileDir' outDir = settings.value(key) home = outDir #outDir = expanduser("~") #filter = (self.DEFAULTPROVIDER + " (*" + # self.DEFAULTEXTENSION + ");;All files (*)") filter = (self.DEFAULTPROVIDER + " (*" + self.DEFAULTEXTENSION + self.EXTRAEXTENSION + ")") #if (self.gdalprovider != self.DEFAULTPROVIDER and # (self.canCreateCopy or # self.canCreate)): # filter = (self.gdalprovider + " (*" + self.gdalext + # ");;" + filter) outFilePath = QFileDialog.getSaveFileName( self, 'Specify file name for skeleton', outDir, filter) outFilePath = unicode(outFilePath) if outFilePath: root, ext = splitext(outFilePath) if ext.lower() != '.tif' and ext.lower() != '.tiff': outFilePath = '%s.tif' % outFilePath outDir = dirname(outFilePath) settings.setValue(key, outDir) # (self.canCreateCopy or self.canCreate): # fileName = splitext(str(fileName))[0]+self.gdalext self.outputRaster.setText(outFilePath) # Overriding def resizeEvent(self, event): #self.showInfo("resizeEvent") self.calculateHistogram() def help(self): #QDesktopServices.openUrl(QUrl.fromLocalFile( # self.plugin_dir + "/help/html/index.html")) showPluginHelp(None, "help/html/index") # Implement the accept method to avoid exiting the dialog when # starting the work def accept(self): """Accept override.""" pass # Implement the reject method to have the possibility to avoid # exiting the dialog when cancelling def reject(self): """Reject override.""" # exit the dialog QDialog.reject(self)
class qgisSpectre: """QGIS Plugin Implementation.""" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = os.path.join(self.plugin_dir, 'i18n', 'qgisSpectre_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Spectre Viewer') self.toolbar = self.iface.addToolBar(u'Spectre viewer') self.toolbar.setObjectName(u'Spectre viewer') self.pluginname = "mortensickel_Spectrumviewer" self.view = MouseReadGraphicsView(self.iface) self.pluginIsActive = False # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('qgisSpectre', message) def add_action(self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar. :param icon_path: Path to the icon for this action. Can be a resource path (e.g. ':/plugins/foo/bar.png') or a normal file system path. :type icon_path: str :param text: Text that should be shown in menu items for this action. :type text: str :param callback: Function to be called when the action is triggered. :type callback: function :param enabled_flag: A flag indicating if the action should be enabled by default. Defaults to True. :type enabled_flag: bool :param add_to_menu: Flag indicating whether the action should also be added to the menu. Defaults to True. :type add_to_menu: bool :param add_to_toolbar: Flag indicating whether the action should also be added to the toolbar. Defaults to True. :type add_to_toolbar: bool :param status_tip: Optional text to show in a popup when mouse pointer hovers over the action. :type status_tip: str :param parent: Parent widget for the new action. Defaults None. :type parent: QWidget :param whats_this: Optional text to show in the status bar when the mouse pointer hovers over the action. :returns: The action that was created. Note that the action is also added to self.actions list. :rtype: QAction """ icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToDatabaseMenu(self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/qgisSpectre/icon.png' self.add_action(icon_path, text=self.tr(u'View Spectra'), callback=self.run, parent=self.iface.mainWindow()) self.dlg = qgisSpectreDockWidget(self.iface.mainWindow()) self.view.setParent(self.dlg) self.dlg.hlMain.addWidget(self.view) #self.dlg.cbLayer.currentIndexChanged['QString'].connect(self.listfields) self.dlg.qgLayer.setFilters(QgsMapLayerProxyModel.VectorLayer) self.dlg.qgField.setLayer(self.dlg.qgLayer.currentLayer()) # self.dlg.qgField.setFilters(QgsFieldProxyModel.Numeric) self.dlg.qgLayer.layerChanged.connect( lambda: self.dlg.qgField.setLayer(self.dlg.qgLayer.currentLayer())) #-------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" #print "** CLOSING qgisSpectre" # disconnects self.dlg.closingPlugin.disconnect(self.onClosePlugin) # remove this statement if dockwidget is to remain # for reuse if plugin is reopened # Commented next statement since it causes QGIS crashe # when closing the docked window: # self.dockwidget = None self.pluginIsActive = False def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" #print "** UNLOAD qgisSpectre" for action in self.actions: self.iface.removePluginDatabaseMenu(self.tr(u'&Spectre Viewer'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def drawspectra(self): """ Drawing the spectra on the graphicsstage """ layername = self.dlg.qgLayer.currentText() fieldname = self.dlg.qgField.currentText() layer = self.dlg.qgLayer.currentLayer() if layer == None: return # Happens some times, just as well to return self.scene.acalib = float(self.dlg.leA.text()) self.scene.bcalib = float(self.dlg.leB.text()) self.scene.unit = self.dlg.leUnit.text() logscale = self.dlg.cbLog.isChecked() if logscale: dataset = [] for n in self.view.spectreval: if n == 0: n = 0.9 dataset.append(math.log(n) - math.log(0.9)) # 0.9 offset and back to be able to plot zero-values else: dataset = self.view.spectreval #DONE: Add x and y axis #DONE: Add scale factors to scale x axis from channel number to keV #DONE: Add settings to have custom unit #TODO: Custom scales #TODO: Keep spectra to compare #DONE: Draw spectra as line, not "line-histogram" #TODO: Select different drawing styles #DONE: Save as file #DONE: export data to clipboard #TODO: export image to clipboard #TODO: Peak detection #TODO: Save different set of calibration values self.scene.h = 300 # Height of stage self.scene.clear() self.scene.crdtext = None self.scene.markerline = None backgroundbrush = QBrush(Qt.white) outlinepen = QPen(Qt.white) # Needs this when saving as png, or background from map will shine through self.scene.addRect(0, 0, 1200, 300, outlinepen, backgroundbrush) self.scene.bottom = 20 # Bottom clearing (for x tick marks and labels) self.scene.left = self.scene.bottom # Left clearing (for y tick marks and labels) n = self.scene.left bt = self.scene.bottom h = self.scene.h self.scene.addLine(float(n - 1), float(h - bt), float(n - 1), 10.0) # Y-axis self.scene.addLine(float(n - 1), float(h - bt - 1), float(len(dataset) + 10), float(h - bt - 1)) # X-axis # Scales the spectra to fit with the size of the graphicsview fact = 1.0 fact = (h - bt - 10) / max(dataset) prevch = 0 for ch in dataset: # TODO: User selectable type of plot # self.scene.addLine(float(n),float(h-bt),float(n),(h-bt-fact*ch)) # self.scene.addLine(float(n),float(h-(bt+4)-fact*ch),float(n),(h-bt-fact*ch)) self.scene.addLine(float(n), float(h - bt - fact * prevch), float(n + 1), (h - bt - fact * ch)) prevch = ch n += 1 self.scene.end = n - 1 tickval = self.tickinterval s = QgsSettings() layername = self.dlg.qgLayer.currentText() fieldname = self.dlg.qgField.currentText() # This is already read in from the fields #acalib=s.value(self.pluginname+"/"+layername+"_"+fieldname+"_a",s.value(self.pluginname+"/defaulta", 1)) #bcalib=s.value(self.pluginname+"/"+layername+"_"+fieldname+"_b",s.value(self.pluginname+"/defaultb", 0)) #self.scene.unit=s.value(self.pluginname+"/"+layername+"_"+fieldname+"_unit",s.value(self.pluginname+"/defaultunit", 0)) acalib = self.scene.acalib bcalib = self.scene.bcalib maxval = acalib * n + bcalib tickdist = tickval #if maxval/n > 5: # Avoids to tight numbering. # tickdist*=2 # Needs some better vay of doing this - left = self.scene.left while tickval < maxval: tickch = (tickval - bcalib) / acalib + left self.scene.addLine(float(tickch), float(h - bt), float(tickch), float(h - bt + 5)) # Ticklines text = self.scene.addText(str(tickval)) text.setPos(tickch + left - 40, 280) tickval += tickdist text = self.scene.addText(self.scene.unit) text.setPos(self.scene.end + 15, 280) ntext = self.scene.addText("n = {}".format(str(self.view.n))) ntext.setPos(self.scene.end + 50, 1) def updatecalib(self): """ Prepares newly typed in calibration values for use """ self.dlg.cbDefault.setChecked(False) layername = self.dlg.qgLayer.currentText() fieldname = self.dlg.qgField.currentText() try: self.scene.acalib = float(self.dlg.leA.text()) self.scene.bcalib = float(self.dlg.leB.text()) except ValueError: if self.dlg.leA.text() == '-' or self.dlg.leB.text( ) == '-' or self.dlg.leA.text() == '' or self.dlg.leB.text() == '': pass else: self.iface.messageBar().pushMessage("Calibrating", "Invalid value(s)", level=Qgis.Warning, duration=3) def setdefault(self): """ Stores actual values as defaults """ s = QgsSettings() if self.dlg.cbDefault.isChecked(): s.setValue(self.pluginname + "/defaulta", self.scene.acalib) s.setValue(self.pluginname + "/defaultb", self.scene.bcalib) s.setValue(self.pluginname + "/defaultunit", self.scene.unit) def usedefault(self): """Fetches default values for the plot """ s = QgsSettings() self.scene.bcalib = s.value(self.pluginname + "/defaultb", 0) self.scene.acalib = s.value(self.pluginname + "/defaulta", 1) self.scene.unit = s.value(self.pluginname + "/defaultunit", 'Ch') self.dlg.leA.setText(str(self.scene.acalib)) self.dlg.leB.setText(str(self.scene.bcalib)) self.dlg.leUnit.setText(str(self.scene.unit)) def prepareplot(self): """ Reads in default values for selected layer before plotting""" layername = self.dlg.qgLayer.currentText() fieldname = self.dlg.qgField.currentText() s = QgsSettings() self.scene.acalib = float( s.value(self.pluginname + "/" + layername + "_" + fieldname + "_a", s.value(self.pluginname + "/defaulta", 1))) self.scene.bcalib = float( s.value(self.pluginname + "/" + layername + "_" + fieldname + "_b", s.value(self.pluginname + "/defaultb", 0))) self.scene.unit = s.value( self.pluginname + "/" + layername + "_" + fieldname + "_unit", s.value(self.pluginname + "/defaultunit", 'Ch')) self.dlg.leA.setText(str(self.scene.acalib)) self.dlg.leB.setText(str(self.scene.bcalib)) self.dlg.leUnit.setText(str(self.scene.unit)) self.findselected() def findselected(self): """ Is being run when points have been selected. Makes a sum spectra from selected points""" layer = self.dlg.qgLayer.currentLayer() if layer == None: return sels = layer.selectedFeatures( ) # The selected features in the active (from this plugin's point of view) layer n = len(sels) if n > 0: #self.iface.messageBar().pushMessage( # "Drawing spectra", "Integrated over {} measurements".format(str(n)), # level=Qgis.Success, duration=3) fieldname = self.dlg.qgField.currentText() # DONE: Rewrite to make it possible to read in a spectra as a string of comma-separated numbers if fieldname == '' or fieldname == None: return # Invalid fieldname, probably not selected yet stringspec = isinstance(sels[0][fieldname], str) stringspec = stringspec and (sels[0][fieldname].find(',') != -1) if isinstance(sels[0][fieldname], list) or stringspec: # Only draw if a list field is selected sumspectre = None for sel in sels: spectre = sel[fieldname] if stringspec: vals = spectre.split(',') spectre = list(map(int, vals)) del spectre[ -1] # To get rid of last channel i.e. cosmic from RSI-spectra # TODO: customable removal of channels at top and/or bottom if sumspectre == None: sumspectre = spectre else: sumspectre = list(map(add, spectre, sumspectre)) self.view.spectreval = sumspectre self.view.n = n self.drawspectra() else: self.iface.messageBar().pushMessage( "Error", "Use an array field or a comma separated string", level=Qgis.Warning, duration=3) def spectreToClipboard(self): """ Copies the channel values to the clipboard as a comma separated string""" clipboard = QApplication.clipboard() text = ",".join(str(x) for x in self.view.spectreval) clipboard.setText(text) # TODO: Make a graphical copy def saveCalibration(self): """ Saves the calibration data """ layername = self.dlg.qgLayer.currentText() fieldname = self.dlg.qgField.currentText() s = QgsSettings() s.setValue(self.pluginname + "/" + layername + "_" + fieldname + "_a", self.scene.acalib) s.setValue(self.pluginname + "/" + layername + "_" + fieldname + "_b", self.scene.bcalib) s.setValue( self.pluginname + "/" + layername + "_" + fieldname + "_unit", self.scene.unit) def run(self): """Run method that loads and starts the plugin""" if not self.pluginIsActive: self.pluginIsActive = True self.scene = QGraphicsScene() # Storing the spectra to be able to read out values later on # Setting the values storing line and text shown when the mouse button is clicked self.scene.crdtext = None self.scene.markerline = None self.scene.left = None # TODO: The four next settings to be user-settable self.tickinterval = 100 s = QgsSettings() self.scene.acalib = s.value(self.pluginname + "/defaulta", 1) self.scene.bcalib = s.value(self.pluginname + "/defaultb", 0) self.scene.unit = s.value(self.pluginname + "/defaultunit", "Ch") self.dlg.leA.setText(str(self.scene.acalib)) self.dlg.leB.setText(str(self.scene.bcalib)) self.dlg.leUnit.setText(str(self.scene.unit)) showch = False # Set to True to show channel values if showch: self.scene.unit = 'Ch' self.scene.acalib = 1 self.scene.bcalib = 0 self.view.setScene(self.scene) self.scene.setSceneRect(0, 0, 1200, 300) # Replotting spectre when a new selection is made self.iface.mapCanvas().selectionChanged.connect(self.findselected) # Listing layers # DONE: Only list vector layers # DONE: Repopulate when layers are added or removed # DONE both by using qgisWidget self.dlg.pBCopy.clicked.connect(self.spectreToClipboard) self.dlg.pBUseDefault.clicked.connect(self.usedefault) self.dlg.pBSaveCalib.clicked.connect(self.saveCalibration) self.dlg.pBSave.clicked.connect(self.view.saveImage) # connect to provide cleanup on closing of dockwidget self.dlg.closingPlugin.connect(self.onClosePlugin) # show the dockwidget self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self.dlg) self.dlg.show() self.dlg.cbLog.stateChanged.connect(self.findselected) self.dlg.cbDefault.stateChanged.connect(self.setdefault) self.dlg.qgField.currentIndexChanged['QString'].connect( self.prepareplot) self.dlg.qgLayer.currentIndexChanged['QString'].connect( self.prepareplot) self.dlg.leA.textChanged['QString'].connect(self.updatecalib) self.dlg.leB.textChanged['QString'].connect(self.updatecalib) self.findselected()
def drawForeground(self, painter, rect): #print "BoreHoleScene.drawForeground" if self.__redraw: with self.__project.connect() as con: cur = con.cursor() self.__redraw = False self.clear() fm = painter.fontMetrics() if self.__id is None: QGraphicsScene.drawForeground(self, painter, rect) return cur.execute( "SELECT geom FROM albion.hole WHERE id='{}'".format( self.__id)) res = cur.fetchone() if not res: QGraphicsScene.drawForeground(self, painter, rect) return hole = wkb.loads(bytes.fromhex(res[0])) line = [p[2] for p in hole.coords] tick_width = 20 spacing = 5 tick_text_offset = -10 tabs = [50, 75, 150, 250, 350, 400, 500] zmin, zmax = min(line), max(line) zpmin, zpmax = 0, ((zmin - zmax) - 5) / self.m_per_pixel text = self.addText(self.__id) text.setPos(tabs[1], -5 * fm.height()) label = 'Depth [m]' text = self.addText(label) text.setRotation(-90) text.setPos(0, 0) text = self.addText('Formation') text.setPos(tabs[1], -3 * fm.height()) text = self.addText('Radiometry') text.setPos(tabs[2], -3 * fm.height()) text = self.addText('Resistivity') text.setPos(tabs[3], -3 * fm.height()) text = self.addText('Mineralization') text.setPos(tabs[4], -3 * fm.height()) top = zpmin - 3 * fm.height() pen = QPen() pen.setWidth(3) for tab in [tabs[1], tabs[2], tabs[3], tabs[4], tabs[6]]: self.addLine(tab, top, tab, zpmax, pen) self.addLine(tabs[1], zpmin, tabs[-1], zpmin, pen) self.addLine(tabs[1], zpmax, tabs[-1], zpmax, pen) self.addLine(tabs[1], top, tabs[-1], top, pen) # depth ticks for z in range(0, int(-(zmax - zmin) - 5), -10): text = "% 4.0f" % (max(line) + z) z /= self.m_per_pixel width = fm.width(text) text = self.addText(text) text.setPos(tabs[0] - width - spacing, tick_text_offset + int(z)) self.addLine(tabs[0], z, tabs[1], z) self.addLine(tabs[2], z, tabs[4], z) #res = cur.execute("SELECT AsText(GEOMETRY), code FROM lithologies WHERE forage={}".format(self.__id)).fetchall() ## litho image #for geom, code in res: # line = [(float(pt.split()[2])-z_tube+h_tube_sol) # for pt in geom.replace('LINESTRING Z(','').replace(')','').split(',')] # z_start = line[0]/self.m_per_pixel # z_end = line[-1]/self.m_per_pixel # brush = QBrush() # brush.setTextureImage(self.texture(code)) # self.addRect(tabs[1], z_start, tabs[2]-tabs[1], z_end-z_start, brush=brush) ## bar diagram grid #for i in range(1, 10): # pen.setWidth(1 if i != 5 else 2) # x = tabs[3]+(tabs[4]-tabs[3])*float(i)/10 # self.addLine(x, zmin, x, zmax, pen) # formation color cur.execute( "SELECT geom, code FROM albion.formation WHERE hole_id='{}'" .format(self.__id)) for geom, code in cur.fetchall(): line = [ p[2] for p in wkb.loads(bytes.fromhex(geom)).coords ] z_start = (line[0] - zmax) / self.m_per_pixel z_end = (line[-1] - zmax) / self.m_per_pixel brush = QBrush() brush.setStyle(Qt.SolidPattern) brush.setColor(self.formation_color(code)) self.addRect(tabs[1], z_start, tabs[2] - tabs[1], z_end - z_start, brush=brush) #width = fm.width(code); #text = self.addText(code) #text.setPos(tabs[2]+spacing, tick_text_offset+int(.5*(z_start+z_end))) self.addLine(tabs[2], z_start, tabs[2], z_start) self.addLine(tabs[2], z_end, tabs[2], z_end) # radiometry diagram cur.execute( "SELECT max(gamma) FROM albion.radiometry WHERE hole_id='{}'" .format(self.__id)) gamma_max = cur.fetchone()[0] cur.execute( "SELECT geom, gamma FROM albion.radiometry WHERE hole_id='{}' AND gamma>=0" .format(self.__id)) for geom, gamma in cur.fetchall(): line = [ p[2] for p in wkb.loads(bytes.fromhex(geom)).coords ] z_start = (line[0] - zmax) / self.m_per_pixel z_end = (line[-1] - zmax) / self.m_per_pixel brush = QBrush() brush.setStyle(Qt.SolidPattern) brush.setColor(QColor(55, 51, 149)) self.addRect(tabs[2], z_start, (tabs[3] - tabs[2]) * gamma / gamma_max, z_end - z_start, pen=QPen(Qt.NoPen), brush=brush) # resistivity diagram cur.execute( "SELECT max(rho) FROM albion.resistivity WHERE hole_id='{}'" .format(self.__id)) rho_max = cur.fetchone()[0] cur.execute( "SELECT geom, rho FROM albion.resistivity WHERE hole_id='{}' AND rho>=0" .format(self.__id)) for geom, rho in cur.fetchall(): line = [ p[2] for p in wkb.loads(bytes.fromhex(geom)).coords ] z_start = (line[0] - zmax) / self.m_per_pixel z_end = (line[-1] - zmax) / self.m_per_pixel brush = QBrush() brush.setStyle(Qt.SolidPattern) brush.setColor(QColor(155, 51, 49)) self.addRect(tabs[3], z_start, (tabs[4] - tabs[3]) * rho / rho_max, z_end - z_start, pen=QPen(Qt.NoPen), brush=brush) # mineralization cur.execute( "SELECT geom, oc, accu, grade FROM albion.mineralization WHERE hole_id='{}'" .format(self.__id)) for geom, oc, accu, grade in cur.fetchall(): line = [ p[2] for p in wkb.loads(bytes.fromhex(geom)).coords ] z_start = (line[0] - zmax) / self.m_per_pixel z_end = (line[-1] - zmax) / self.m_per_pixel brush = QBrush() brush.setStyle(Qt.SolidPattern) brush.setColor(QColor(250, 250, 50)) self.addRect(tabs[4], z_start, tabs[5] - tabs[4], z_end - z_start, brush=brush) txt = "oc=" + str(oc) + "\naccu=" + str( accu) + "\ngrade=" + str(grade) width = fm.width(txt) text = self.addText(txt) text.setPos( tabs[5] + spacing, -int(1.5 * fm.height()) + int(.5 * (z_start + z_end))) self.addLine(tabs[4], z_start, tabs[6], z_start) self.addLine(tabs[4], z_end, tabs[6], z_end) self.setSceneRect(self.itemsBoundingRect()) QGraphicsScene.drawForeground(self, painter, rect)