class ProcessingWdg(QWidget, Ui_ProcessingWdg): def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface self.iface = iface self.canvas = self.iface.mapCanvas() self._prevMapTool = None self.algorithmStacked.setCurrentIndex(0) # create the maptool to draw polygons self.polygonDrawer = PolygonDrawer(self.canvas, { 'color': QColor("#666666"), 'enableSnap': False, 'keepAfterEnd': True }) self.polygonDrawer.setAction(self.drawPolygonBtn) self.connect(self.polygonDrawer, SIGNAL("geometryEmitted"), self.polygonCreated) # connect actions to the widgets self.connect(self.drawPolygonBtn, SIGNAL("clicked()"), self.drawPolygon) self.connect(self.clearPolygonBtn, SIGNAL("clicked()"), self.clearPolygon) QObject.connect(self.declusterWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.completenessWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.recurrenceWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.maxMagnitudeWdg, SIGNAL("dataRequested"), self.fillData) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool not in (self.polygonDrawer, ): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.polygonDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self self.clearPolygon() # restore the previous maptool self.restorePrevMapTool() # delete the polygon drawer maptool self.polygonDrawer.deleteLater() self.polygonDrawer = None QWidget.deleteLater(self, *args) def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.polygonDrawer.isEmittingPoints: self.polygonDrawer.reset() else: self.polygonDrawer.rubberBand.show( ) if show else self.polygonDrawer.rubberBand.hide() def drawPolygon(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.polygonDrawer.startCapture() def clearPolygon(self): # remove the displayed polygon self.polygonDrawer.reset() def polygonCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def fillData(self, data, panMap): """ fetch data from the classified layer and put them into the passed arguments: **data** list will contain the selected features attributes, **panMap** dictionary will keep the association between fields and their position in the data list """ classifiedVl = Utils.classifiedVl() pr = classifiedVl.dataProvider() # spatial filter spatialFilter = self.polygonDrawer.geometry() if not spatialFilter: extent = QgsRectangle() else: # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), classifiedVl.crs()) ret = spatialFilter.transform(toLayerCrsTransform) if ret != 0: QMessageBox.warning( self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return extent = spatialFilter.boundingBox() index2key = Utils.index2keyFieldMap(pr.fields()) # fetch and loop through the features pr = classifiedVl.dataProvider() pr.select(classifiedVl.pendingAllAttributesList(), extent, True) f = QgsFeature() while pr.nextFeature(f): # filter features by spatial filter if spatialFilter and not spatialFilter.contains(f.geometry()): continue # create a new row: [id, *fields] attrs = f.attributeMap() row = [f.id()] for index, val in sorted(attrs.iteritems()): row.append(attrs[index]) # store the index of well-known field in the pan map so they # can be found easily later key = index2key.get(index, None) if key is not None: panMap[key] = len(row) - 1 # append to the data buffer data.append(row) if len(data) <= 0: QMessageBox.information(self, "Processing", "No features in the result") return
class FilterWdg(QWidget, Ui_FilterWdg): PLOT_HIST, PLOT_HIST_LOG, PLOT_SCATTER = range(3) def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface and the event layer self.iface = iface self.vl = Utils.eventsVl() self.canvas = self.iface.mapCanvas() self._prevMapTool = None # setup the magnitude, depth and range filters self.setupFilters() # create the maptool to draw polygons self.polygonDrawer = PolygonDrawer(self.canvas, { 'color': QColor("#666666"), 'enableSnap': False, 'keepAfterEnd': True }) self.polygonDrawer.setAction(self.drawPolygonBtn) self.connect(self.polygonDrawer, SIGNAL("geometryEmitted"), self.polygonCreated) # populate plot type combo self.plotTypeCombo.addItem("Histogram", self.PLOT_HIST) self.plotTypeCombo.addItem("Histogram (log10)", self.PLOT_HIST_LOG) self.plotTypeCombo.addItem("Scatter plot", self.PLOT_SCATTER) self.updateAxesCombos() # populate both axis combos with field names for index, fld in self.vl.dataProvider().fields().iteritems(): self.xAxisCombo.addItem(fld.name(), QVariant(index)) self.yAxisCombo.addItem(fld.name(), QVariant(index)) # connect actions to the widgets self.connect(self.drawPolygonBtn, SIGNAL("clicked()"), self.drawPolygon) self.connect(self.clearPolygonBtn, SIGNAL("clicked()"), self.clearPolygon) self.connect(self.plotBtn, SIGNAL("clicked()"), self.createPlot) self.connect(self.plotTypeCombo, SIGNAL("currentIndexChanged(int)"), self.updateAxesCombos) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool not in (self.polygonDrawer, ): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.polygonDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self self.clearPolygon() # restore the previous maptool self.restorePrevMapTool() # delete the polygon drawer maptool self.polygonDrawer.deleteLater() self.polygonDrawer = None QWidget.deleteLater(self, *args) def _filterForKey(self, key): if key == 'magnitude': return self.magnitudeRangeFilter if key == 'depth': return self.depthRangeFilter if key == 'date': return self.dateRangeFilter return None def _hasPlotYField(self, plotType): return plotType not in (self.PLOT_HIST, self.PLOT_HIST_LOG) def _getPlotType(self): return self.plotTypeCombo.itemData(self.plotTypeCombo.currentIndex()) def updateAxesCombos(self): self.yAxisCombo.setEnabled(self._hasPlotYField(self._getPlotType())) def setupFilters(self): # setup the magnitude, depth and range filters self.magnitudeRangeFilter.setEnabled(False) self.magnitudeRangeFilter.setOrientation(Qt.Horizontal) self.magnitudeRangeFilter.setDecimals(1) self.magnitudeRangeFilter.setMinimum(0) self.magnitudeRangeFilter.setMaximum(10) self.depthRangeFilter.setEnabled(False) self.depthRangeFilter.setOrientation(Qt.Horizontal) self.depthRangeFilter.setMinimum(0) self.depthRangeFilter.setMaximum(1000) self.dateRangeFilter.setEnabled(False) self.dateRangeFilter.setOrientation(Qt.Horizontal) # take min/max values index from the data provider, # buf first let's reset the subset string to get real min/max pr = self.vl.dataProvider() pr.setSubsetString("") key2index = Utils.key2indexFieldMap(pr.fields()) for key, index in key2index.iteritems(): filterWdg = self._filterForKey(key) if not filterWdg: continue minVal = pr.minimumValue(index) maxVal = pr.maximumValue(index) if minVal.isValid() and maxVal.isValid(): # check for empty strings (the provider could be unable to retrieve values) if minVal.toString().isEmpty(): minVal = None if maxVal.toString().isEmpty(): maxVal = None if minVal is None or maxVal is None: # let's search the real min/max values self.vl.select([index], QgsRectangle(), False) f = QgsFeature() while self.vl.nextFeature(f): fval = Utils.valueFromQVariant(f.attributeMap()[index]) if not minVal or fval < minVal: minVal = fval if not maxVal or fval > maxVal: maxVal = fval else: minVal = Utils.valueFromQVariant(minVal) maxVal = Utils.valueFromQVariant(maxVal) # setup filter min/max range try: filterWdg.setMinimum(minVal) filterWdg.setLowValue(minVal) filterWdg.setMaximum(maxVal) filterWdg.setHighValue(maxVal) except: raise # unable to set min/max, skip the filter continue filterWdg.setEnabled(True) self.connect(filterWdg, SIGNAL("changeFinished"), self.updateMap) # filter default ranges self.magnitudeRangeFilter.setLowValue(3.5) self.magnitudeRangeFilter.setHighValue(10) self.depthRangeFilter.setLowValue(0) self.depthRangeFilter.setHighValue(250) self.updateMap() def updateMap(self, *args): pr = self.vl.dataProvider() key2index = Utils.key2indexFieldMap(pr.fields()) # convert filters to subset string subsets = [] for key, index in key2index.iteritems(): filterWdg = self._filterForKey(key) if not filterWdg or not filterWdg.isEnabled( ) or not filterWdg.isActive(): continue name = pr.fields()[index].name() # define a new subset string when the low value is greather then the minimum value if filterWdg.lowValue() > filterWdg.minimum(): minVal = QVariant(filterWdg.lowValue()) if minVal.type() in (QVariant.Date, QVariant.DateTime): dataFormat = "yyyy/MM/dd" if self.vl.providerType( ) == 'ogr' else "yyyy-MM-dd" minVal = u"'%s'" % minVal.toDate().toString(dataFormat) else: minVal = minVal.toString() subsets.append(u"\"%s\" >= %s" % (name.replace('"', '""'), minVal)) # define a new subset string when the high value is less then the maximum value if filterWdg.highValue() < filterWdg.maximum(): maxVal = QVariant(filterWdg.highValue()) if maxVal.type() in (QVariant.Date, QVariant.DateTime): dataFormat = "yyyy/MM/dd" if self.vl.providerType( ) == 'ogr' else "yyyy-MM-dd" maxVal = u"'%s'" % maxVal.toDate().toString(dataFormat) else: maxVal = maxVal.toString() subsets.append(u"\"%s\" <= %s" % (name.replace('"', '""'), maxVal)) # set the subset string, then update the layer if self.vl.setSubsetString(u" AND ".join(subsets)): self.vl.triggerRepaint() def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.polygonDrawer.isEmittingPoints: self.polygonDrawer.reset() else: self.polygonDrawer.rubberBand.show( ) if show else self.polygonDrawer.rubberBand.hide() def drawPolygon(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.polygonDrawer.startCapture() def clearPolygon(self): # remove the displayed polygon self.polygonDrawer.reset() def polygonCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def createPlot(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: dlg = self._createPlotDlg() finally: QApplication.restoreOverrideCursor() if dlg: dlg.show() dlg.exec_() dlg.deleteLater() def _createPlotDlg(self): plotType = self._getPlotType() hasYField = self._hasPlotYField(plotType) # spatial filter spatialFilter = self.polygonDrawer.geometry() if not spatialFilter: extent = QgsRectangle() else: # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs()) ret = spatialFilter.transform(toLayerCrsTransform) if ret != 0: QMessageBox.warning( self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return extent = spatialFilter.boundingBox() # get the indexes of fields selected in the combos xIndex = self.xAxisCombo.itemData( self.xAxisCombo.currentIndex()).toInt()[0] yIndex = self.yAxisCombo.itemData( self.yAxisCombo.currentIndex()).toInt()[0] pr = self.vl.dataProvider() index2key = Utils.index2keyFieldMap(pr.fields()) indexes = [] for index, fld in pr.fields().iteritems(): if index == xIndex: indexes.append(xIndex) continue if index == yIndex and hasYField: indexes.append(yIndex) continue filterWdg = self._filterForKey(Utils.fieldName2key(fld.name())) if filterWdg and filterWdg.isActive(): indexes.append(index) # the following lists will contain x and y values x, y = ([], []) # fetch and loop through the features pr.select(indexes, extent, True) f = QgsFeature() while pr.nextFeature(f): # filter features by spatial filter if spatialFilter and not spatialFilter.contains(f.geometry()): continue # filter features by attribute values attrs = f.attributeMap() ok = True for index, val in attrs.iteritems(): if not index2key.has_key(index): continue # unused field filterWdg = self._filterForKey(index2key[index]) if not filterWdg: continue # the field has no associated filter widget if filterWdg.isActive() and not filterWdg.checkValue(val): ok = False break if not ok: # the feature was filtered out continue # append feature values to the lists x.append(attrs[xIndex]) if hasYField: y.append(attrs[yIndex]) if len(x) <= 0: QMessageBox.information(self, "Plot", "No features in the result") return from plot_wdg import HistogramPlotDlg, ScatterPlotDlg if plotType == self.PLOT_HIST: dlg = HistogramPlotDlg( None, [x], [self.xAxisCombo.currentText(), "Frequency"], self.titleEdit.text()) elif plotType == self.PLOT_HIST_LOG: dlg = HistogramPlotDlg( None, [x], [self.xAxisCombo.currentText(), "Frequency"], self.titleEdit.text(), {'yscale': 'log'}) elif plotType == self.PLOT_SCATTER: dlg = ScatterPlotDlg( None, [x, y], [self.xAxisCombo.currentText(), self.yAxisCombo.currentText()], self.titleEdit.text()) else: return return dlg
class FilterWdg(QWidget, Ui_FilterWdg): PLOT_HIST, PLOT_HIST_LOG, PLOT_SCATTER = range(3) def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface and the event layer self.iface = iface self.vl = Utils.eventsVl() self.canvas = self.iface.mapCanvas() self._prevMapTool = None # setup the magnitude, depth and range filters self.setupFilters() # create the maptool to draw polygons self.polygonDrawer = PolygonDrawer( self.canvas, {'color':QColor("#666666"), 'enableSnap':False, 'keepAfterEnd':True} ) self.polygonDrawer.setAction( self.drawPolygonBtn ) self.connect(self.polygonDrawer, SIGNAL("geometryEmitted"), self.polygonCreated) # populate plot type combo self.plotTypeCombo.addItem( "Histogram", self.PLOT_HIST ) self.plotTypeCombo.addItem( "Histogram (log10)", self.PLOT_HIST_LOG ) self.plotTypeCombo.addItem( "Scatter plot", self.PLOT_SCATTER ) self.updateAxesCombos() # populate both axis combos with field names for index, fld in self.vl.dataProvider().fields().iteritems(): self.xAxisCombo.addItem( fld.name(), QVariant(index) ) self.yAxisCombo.addItem( fld.name(), QVariant(index) ) # connect actions to the widgets self.connect(self.drawPolygonBtn, SIGNAL("clicked()"), self.drawPolygon) self.connect(self.clearPolygonBtn, SIGNAL("clicked()"), self.clearPolygon) self.connect(self.plotBtn, SIGNAL("clicked()"), self.createPlot) self.connect(self.plotTypeCombo, SIGNAL("currentIndexChanged(int)"), self.updateAxesCombos) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool not in (self.polygonDrawer,): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.polygonDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self self.clearPolygon() # restore the previous maptool self.restorePrevMapTool() # delete the polygon drawer maptool self.polygonDrawer.deleteLater() self.polygonDrawer = None QWidget.deleteLater(self, *args) def _filterForKey(self, key): if key == 'magnitude': return self.magnitudeRangeFilter if key == 'depth': return self.depthRangeFilter if key == 'date': return self.dateRangeFilter return None def _hasPlotYField(self, plotType): return plotType not in (self.PLOT_HIST, self.PLOT_HIST_LOG) def _getPlotType(self): return self.plotTypeCombo.itemData( self.plotTypeCombo.currentIndex() ) def updateAxesCombos(self): self.yAxisCombo.setEnabled( self._hasPlotYField( self._getPlotType() ) ) def setupFilters(self): # setup the magnitude, depth and range filters self.magnitudeRangeFilter.setEnabled(False) self.magnitudeRangeFilter.setOrientation( Qt.Horizontal ) self.magnitudeRangeFilter.setDecimals( 1 ) self.magnitudeRangeFilter.setMinimum( 0 ) self.magnitudeRangeFilter.setMaximum( 10 ) self.depthRangeFilter.setEnabled(False) self.depthRangeFilter.setOrientation( Qt.Horizontal ) self.depthRangeFilter.setMinimum( 0 ) self.depthRangeFilter.setMaximum( 1000 ) self.dateRangeFilter.setEnabled(False) self.dateRangeFilter.setOrientation( Qt.Horizontal ) # take min/max values index from the data provider, # buf first let's reset the subset string to get real min/max pr = self.vl.dataProvider() pr.setSubsetString("") key2index = Utils.key2indexFieldMap( pr.fields() ) for key, index in key2index.iteritems(): filterWdg = self._filterForKey( key ) if not filterWdg: continue minVal = pr.minimumValue( index ) maxVal = pr.maximumValue( index ) if minVal.isValid() and maxVal.isValid(): # check for empty strings (the provider could be unable to retrieve values) if minVal.toString().isEmpty(): minVal = None if maxVal.toString().isEmpty(): maxVal = None if minVal is None or maxVal is None: # let's search the real min/max values self.vl.select([index], QgsRectangle(), False) f = QgsFeature() while self.vl.nextFeature( f ): fval = Utils.valueFromQVariant( f.attributeMap()[index] ) if not minVal or fval < minVal: minVal = fval if not maxVal or fval > maxVal: maxVal = fval else: minVal = Utils.valueFromQVariant( minVal ) maxVal = Utils.valueFromQVariant( maxVal ) # setup filter min/max range try: filterWdg.setMinimum( minVal ) filterWdg.setLowValue( minVal ) filterWdg.setMaximum( maxVal ) filterWdg.setHighValue( maxVal ) except: raise # unable to set min/max, skip the filter continue filterWdg.setEnabled(True) self.connect(filterWdg, SIGNAL("changeFinished"), self.updateMap) # filter default ranges self.magnitudeRangeFilter.setLowValue( 3.5 ) self.magnitudeRangeFilter.setHighValue( 10 ) self.depthRangeFilter.setLowValue( 0 ) self.depthRangeFilter.setHighValue( 250 ) self.updateMap() def updateMap(self, *args): pr = self.vl.dataProvider() key2index = Utils.key2indexFieldMap( pr.fields() ) # convert filters to subset string subsets = [] for key, index in key2index.iteritems(): filterWdg = self._filterForKey( key ) if not filterWdg or not filterWdg.isEnabled() or not filterWdg.isActive(): continue name = pr.fields()[index].name() # define a new subset string when the low value is greather then the minimum value if filterWdg.lowValue() > filterWdg.minimum(): minVal = QVariant(filterWdg.lowValue()) if minVal.type() in (QVariant.Date, QVariant.DateTime): dataFormat = "yyyy/MM/dd" if self.vl.providerType() == 'ogr' else "yyyy-MM-dd" minVal = u"'%s'" % minVal.toDate().toString(dataFormat) else: minVal = minVal.toString() subsets.append( u"\"%s\" >= %s" % (name.replace('"', '""'), minVal) ) # define a new subset string when the high value is less then the maximum value if filterWdg.highValue() < filterWdg.maximum(): maxVal = QVariant(filterWdg.highValue()) if maxVal.type() in (QVariant.Date, QVariant.DateTime): dataFormat = "yyyy/MM/dd" if self.vl.providerType() == 'ogr' else "yyyy-MM-dd" maxVal = u"'%s'" % maxVal.toDate().toString(dataFormat) else: maxVal = maxVal.toString() subsets.append( u"\"%s\" <= %s" % (name.replace('"', '""'), maxVal) ) # set the subset string, then update the layer if self.vl.setSubsetString( u" AND ".join(subsets) ): self.vl.triggerRepaint() def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.polygonDrawer.isEmittingPoints: self.polygonDrawer.reset() else: self.polygonDrawer.rubberBand.show() if show else self.polygonDrawer.rubberBand.hide() def drawPolygon(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.polygonDrawer.startCapture() def clearPolygon(self): # remove the displayed polygon self.polygonDrawer.reset() def polygonCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def createPlot(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: dlg = self._createPlotDlg() finally: QApplication.restoreOverrideCursor() if dlg: dlg.show() dlg.exec_() dlg.deleteLater() def _createPlotDlg(self): plotType = self._getPlotType() hasYField = self._hasPlotYField( plotType ) # spatial filter spatialFilter = self.polygonDrawer.geometry() if not spatialFilter: extent = QgsRectangle() else: # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs() ) ret = spatialFilter.transform( toLayerCrsTransform ) if ret != 0: QMessageBox.warning(self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return extent = spatialFilter.boundingBox() # get the indexes of fields selected in the combos xIndex = self.xAxisCombo.itemData( self.xAxisCombo.currentIndex() ).toInt()[0] yIndex = self.yAxisCombo.itemData( self.yAxisCombo.currentIndex() ).toInt()[0] pr = self.vl.dataProvider() index2key = Utils.index2keyFieldMap( pr.fields() ) indexes = [] for index, fld in pr.fields().iteritems(): if index == xIndex: indexes.append( xIndex ) continue if index == yIndex and hasYField: indexes.append( yIndex ) continue filterWdg = self._filterForKey( Utils.fieldName2key( fld.name() ) ) if filterWdg and filterWdg.isActive(): indexes.append( index ) # the following lists will contain x and y values x, y = ([], []) # fetch and loop through the features pr.select( indexes, extent, True ) f = QgsFeature() while pr.nextFeature( f ): # filter features by spatial filter if spatialFilter and not spatialFilter.contains( f.geometry() ): continue # filter features by attribute values attrs = f.attributeMap() ok = True for index, val in attrs.iteritems(): if not index2key.has_key( index ): continue # unused field filterWdg = self._filterForKey( index2key[ index ] ) if not filterWdg: continue # the field has no associated filter widget if filterWdg.isActive() and not filterWdg.checkValue( val ): ok = False break if not ok: # the feature was filtered out continue # append feature values to the lists x.append( attrs[ xIndex ] ) if hasYField: y.append( attrs[ yIndex ] ) if len(x) <= 0: QMessageBox.information(self, "Plot", "No features in the result") return from plot_wdg import HistogramPlotDlg, ScatterPlotDlg if plotType == self.PLOT_HIST: dlg = HistogramPlotDlg(None, [x], [self.xAxisCombo.currentText(), "Frequency"], self.titleEdit.text()) elif plotType == self.PLOT_HIST_LOG: dlg = HistogramPlotDlg(None, [x], [self.xAxisCombo.currentText(), "Frequency"], self.titleEdit.text(), {'yscale':'log'}) elif plotType == self.PLOT_SCATTER: dlg = ScatterPlotDlg(None, [x, y], [self.xAxisCombo.currentText(), self.yAxisCombo.currentText()], self.titleEdit.text()) else: return return dlg
class ClassificationWdg(QWidget, Ui_ClassificationWdg): def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface and the events layer self.iface = iface self.vl = Utils.eventsVl() self.canvas = self.iface.mapCanvas() self._prevMapTool = None self._sharedData = { } # it will contain the classification map (data shared through the all cross sections) # create the maptool to define the area of interest self.areaDrawer = PolygonDrawer( self.iface.mapCanvas(), { 'color': QColor('#333333'), 'border': 2, 'enableSnap': False, 'keepAfterEnd': True }) self.areaDrawer.setAction(self.drawAreaBtn) self.connect(self.areaDrawer, SIGNAL("geometryEmitted"), self.areaCreated) # create the maptool to create classification buffers self.segmentDrawer = SegmentDrawer(self.iface.mapCanvas(), { 'color': QColor('cyan'), 'border': 3, 'enableSnap': False }) self.segmentDrawer.setAction(self.addBufferBtn) self.connect(self.segmentDrawer, SIGNAL("geometryEmitted"), self.midlineBufferCreated) # initialize the table that will contain classification buffers self.buffersTable.setModel(self.BuffersTableModel(self.buffersTable)) self.connect( self.buffersTable.selectionModel(), SIGNAL( "currentRowChanged(const QModelIndex &, const QModelIndex &)"), self.buffersSelectionChanged) self.connect(self.buffersTable.model(), SIGNAL("bufferWidthChanged"), self.updateBuffer) # connect actions to the widgets self.connect(self.drawAreaBtn, SIGNAL("clicked()"), self.drawArea) self.connect(self.clearAreaBtn, SIGNAL("clicked()"), self.clearArea) self.connect(self.addBufferBtn, SIGNAL("clicked()"), self.drawBuffer) self.connect(self.delBufferBtn, SIGNAL("clicked()"), self.deleteBuffer) self.connect(self.crossSectionBtn, SIGNAL("clicked()"), self.openCrossSection) self.connect(self.displayClassifiedDataBtn, SIGNAL("clicked()"), self.loadClassifiedData) # disable buttons self.delBufferBtn.setEnabled(False) self.crossSectionBtn.setEnabled(False) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool and prevMapTool not in (self.areaDrawer, self.segmentDrawer): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.areaDrawer.stopCapture() self.segmentDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self # clear rubberbands self.clearArea() self.clearBuffers() # restore the previous maptool self.restorePrevMapTool() # delete the maptools self.areaDrawer.deleteLater() self.areaDrawer = None self.segmentDrawer.deleteLater() self.segmentDrawer = None return QWidget.deleteLater(self, *args) def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.areaDrawer.isEmittingPoints: self.areaDrawer.reset() else: self.areaDrawer.rubberBand.show( ) if show else self.areaDrawer.rubberBand.hide() self.segmentDrawer.reset() model = self.buffersTable.model() for row in range(model.rowCount()): # get item data index = model.index(row, 0) buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineRb.show() if show else midlineRb.hide() bufferRb.show() if show else bufferRb.hide() if dlg: dlg.hide() def drawArea(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.areaDrawer.startCapture() def clearArea(self): # remove the displayed polygon self.areaDrawer.reset() def areaCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def drawBuffer(self): """ set the maptool to draw the buffer midline """ # store the previous maptool self.storePrevMapTool() # set the segment drawer as current maptool self.segmentDrawer.startCapture() def clearBuffers(self): """ delete all the classification buffers and related rubberbands """ self.segmentDrawer.reset() model = self.buffersTable.model() for row in range(model.rowCount() - 1, -1, -1): self.deleteBuffer(row) self.buffersTable.model().clear() def midlineBufferCreated(self, line): """ called after the buffer midline is drawn by the user """ # restore the previous maptool self.restorePrevMapTool() # check for a valid segment if not line: return segment = line.asPolyline() if len(segment) != 2: return # create the midline rubber band midlineRb = QgsRubberBand(self.canvas, False) midlineRb.setColor(QColor('cyan')) midlineRb.setWidth(3) # create the buffer rubber band bufferRb = QgsRubberBand(self.canvas, True) bufferRb.setColor(QColor('fuchsia')) bufferRb.setWidth(1) # define buffer width in layer CRS unit units = self.canvas.mapUnits() if units == QGis.Meters: bufferwidth = 100000 # 100 Km elif units == QGis.Feet: bufferwidth = 300000 # 300 Kft else: bufferwidth = 1.0 # 1 degree # append the new classification buffer to the table data = [segment, (midlineRb, bufferRb), None] row = self.buffersTable.model().append(bufferwidth, data) self.buffersTable.setCurrentIndex(self.buffersTable.model().index( row, 0)) def redrawMidlineRubberBand(self, rubberBand, segment): """ re-draw the midline rubberband by segment """ rubberBand.reset(False) # add points to the midline rubber band rubberBand.addPoint(segment[0], False) rubberBand.addPoint(segment[1], True) rubberBand.show() def redrawBufferRubberBand(self, rubberBand, segment, width): """ re-draw the buffer rubberband by segment and width """ rubberBand.reset(True) # create a buffer around the line import math (x1, y1), (x2, y2) = segment angle = math.atan(float(y2 - y1) / (x2 - x1)) if x2 - x1 != 0 else math.pi / 2 xincr, yincr = width * math.sin(angle), width * math.cos(angle) # add the buffer geom points to the rubber band rubberBand.addPoint(QgsPoint(x1 + xincr, y1 - yincr), False) rubberBand.addPoint(QgsPoint(x1 - xincr, y1 + yincr), False) rubberBand.addPoint(QgsPoint(x2 - xincr, y2 + yincr), False) rubberBand.addPoint(QgsPoint(x2 + xincr, y2 - yincr), True) rubberBand.show() def deleteBuffer(self, row=None): """ delete the classification buffer at row and its rubberbands """ model = self.buffersTable.model() if row is None: row = self.buffersTable.currentIndex().row() if row < 0 or row >= model.rowCount(): return # get item data segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(model.index(row, 0)) # delete the item model.removeRows(row, 1) # destroy item data midlineRb.reset(False) bufferRb.reset(True) if dlg: self.disconnect(self, SIGNAL("classificationUpdated"), dlg.refresh) dlg.close() dlg.deleteLater() def updateBuffer(self, index): """ re-draw the rubberbands for the classification buffer at index """ model = self.buffersTable.model() buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) self.redrawBufferRubberBand(bufferRb, segment, buffersize) self.redrawMidlineRubberBand(midlineRb, segment) if dlg: dlg.refresh() def buffersSelectionChanged(self, current, previous): """ update buttons, then highlight the rubberbands for the selected classification buffer """ self.delBufferBtn.setEnabled(current.isValid()) self.crossSectionBtn.setEnabled(current.isValid()) model = self.buffersTable.model() for index in (previous, current): if not index.isValid(): continue buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineRb.setColor( QColor('#88FFFF')) if index == current else midlineRb.setColor( QColor('#00FFFF')) # color cyan bufferRb.setColor( QColor('#FF88FF')) if index == current else bufferRb.setColor( QColor('#FF00FF')) # color fuchsia self.redrawBufferRubberBand(bufferRb, segment, buffersize) self.redrawMidlineRubberBand(midlineRb, segment) def openCrossSection(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: dlg = self._openCrossSection() finally: QApplication.restoreOverrideCursor() def _openCrossSection(self): """ open a cross section displaying geometries within this classification buffer """ index = self.buffersTable.currentIndex() if not index.isValid(): return # get the geometry which defines the area of interest areaGeom = self.areaDrawer.geometry() if not areaGeom or areaGeom.isGeosEmpty(): QMessageBox.warning( self, "No Area of interest", "Define an Area of interest and then try again.") return # get midline and area geometries for the selected classification buffer model = self.buffersTable.model() segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineGeom = midlineRb.asGeometry() bufferGeom = bufferRb.asGeometry() # compute the spatial filter filteringGeom = bufferGeom.intersection(areaGeom) if not filteringGeom or filteringGeom.isGeosEmpty(): QMessageBox.warning( self, "Invalid area", "Intersection between the Area of interest and the selected classification buffer area is invalid or empty." ) return # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs()) ret = filteringGeom.transform(toLayerCrsTransform) if ret != 0: QMessageBox.warning( self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return # get the depth field index pr = self.vl.dataProvider() key2index = Utils.key2indexFieldMap(pr.fields()) depthIndex = key2index['depth'] # the following x and y lists will contain respectively the distance # along the buffer and the depth, the info list instead will contain # additional data x, y, info = [], [], [] # fetch and loop through the features pr.select([depthIndex], filteringGeom.boundingBox(), True) toMapCrsTransform = QgsCoordinateTransform( self.vl.crs(), self.canvas.mapRenderer().destinationCrs()) f = QgsFeature() while pr.nextFeature(f): geom = f.geometry() # filter features by spatial filter if not filteringGeom.contains(geom): continue # transform to map CRS if geom.transform(toMapCrsTransform) != 0: continue geom = QgsGeometry.fromPoint(geom.asPoint()) # store distance and depth values dist = Utils.distanceAlongProfile(midlineGeom, geom) x.append(Utils.toDisplayedSize(dist)) # convert to Km/Kft/degrees y.append(f.attributeMap()[depthIndex].toDouble()[0] * -1) info.append(f.id()) if len(x) <= 0: QMessageBox.information(self, "Cross section", "No features in the result") return # plot now! if not dlg: from cross_section_wdg import CrossSectionDlg dlg = CrossSectionDlg(self._sharedData, self) self.connect(dlg, SIGNAL("classificationUpdateRequested"), self.updateClassification) self.connect(self, SIGNAL("classificationUpdated"), dlg.refresh) # store the dialog into the item data model.setAdditionalData(index, [segment, (midlineRb, bufferRb), dlg]) dlg.setData(x, y, info) dlg.setLabels("Distance", "Depth") self.updateClassification() dlg.show() def updateClassification(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: self._updateClassification() finally: QApplication.restoreOverrideCursor() def _updateClassification(self): model = self.buffersTable.model() # update the earthquakes classification self._sharedData.clear() for row in range(model.rowCount()): dlg = model.getAdditionalData(model.index(row, 0))[2] if not dlg: continue classified = (shallow, deep) = dlg.classify() # update the shared data for typeIndex, data in enumerate(classified): for fid in data: # avoid duplicates if self._sharedData.has_key(fid): # do not update shallow earthquakes if self._sharedData[fid] == 0: continue # already present self._sharedData[fid] = typeIndex self.emit(SIGNAL("classificationUpdated")) def loadClassifiedData(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) # store the current render flag state, then unset it prev_render_flag = self.iface.mapCanvas().renderFlag() self.iface.mapCanvas().setRenderFlag(False) try: self._loadClassifiedData() finally: # restore render flag state and cursor self.iface.mapCanvas().setRenderFlag(prev_render_flag) QApplication.restoreOverrideCursor() def _loadClassifiedData(self): # get the geometry which defines the area of interest areaGeom = self.areaDrawer.geometry() if not areaGeom or areaGeom.isGeosEmpty(): QMessageBox.warning( self, "No Area of interest", "Define an Area of interest and then try again.") return # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs()) ret = areaGeom.transform(toLayerCrsTransform) if ret != 0: QMessageBox.warning( self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return # update the classification self.updateClassification() classifiedVl = Utils.classifiedVl() outpr = classifiedVl.dataProvider() # remove all the features in the classified events layer classifiedVl.removeSelection() classifiedVl.invertSelection() outpr.deleteFeatures(classifiedVl.selectedFeaturesIds()) # fetch and loop through the features inpr = self.vl.dataProvider() inpr.select(self.vl.pendingAllAttributesList(), areaGeom.boundingBox(), True) betweenLayersCrsTransform = QgsCoordinateTransform( self.vl.crs(), classifiedVl.crs()) f = QgsFeature() while inpr.nextFeature(f): geom = f.geometry() # filter features by spatial filter if not areaGeom.contains(geom): continue # skip unclassified data if f.id() not in self._sharedData: continue classType = self._sharedData[f.id()] # transform to classified layer CRS if geom.transform(betweenLayersCrsTransform) != 0: continue f.setGeometry(QgsGeometry.fromPoint(geom.asPoint())) # set feature attributes attrs = {} for index, attr in sorted(f.attributeMap().iteritems()): attrs[len(attrs)] = attr attrs[len(attrs)] = 'shallow' if classType == 0 else 'deep' f.setAttributeMap(attrs) # add the feature outpr.addFeatures([f]) # update layer's extent when new features have been added # because change of extent in provider is not propagated to the layer classifiedVl.updateExtents() # add the layer to the map Utils.addVectorLayer(classifiedVl) class BuffersTableModel(QStandardItemModel): def __init__(self, parent=None): self.header = ["Buffer width"] QStandardItemModel.__init__(self, 0, len(self.header), parent) def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return QVariant(self.header[section]) if orientation == Qt.Vertical: return QVariant(section + 1) return QVariant() def append(self, buffersize, data): item = QStandardItem() item.setFlags(item.flags() | Qt.ItemIsEditable) self.appendRow([item]) row = self.rowCount() - 1 # store buffersize and additional data self.blockSignals(True) self.setBufferSize(self.index(row, 0), buffersize) self.setAdditionalData(self.index(row, 0), data) self.blockSignals(False) # a new buffer was added, trigger for its repainting self.emit(SIGNAL("bufferWidthChanged"), self.index(row, 0)) return row def setData(self, index, data, role=Qt.EditRole): ret = QStandardItemModel.setData(self, index, data, role) if role == Qt.EditRole and index.column() == 0: self.emit(SIGNAL("bufferWidthChanged"), index) return ret def getBufferSize(self, index): if index.isValid(): displayed_size = self.data(self.index(index.row(), 0)).toDouble()[0] # convert back to m/ft/degrees return Utils.fromDisplayedSize(displayed_size) def setBufferSize(self, index, buffersize): if index.isValid(): # display Km/Kft/degrees displayed_size = Utils.toDisplayedSize(buffersize) self.setData(self.index(index.row(), 0), QVariant(displayed_size)) def getAdditionalData(self, index): if index.isValid(): return self.data(self.index(index.row(), 0), Qt.UserRole).toPyObject() def setAdditionalData(self, index, data): if index.isValid(): self.setData(self.index(index.row(), 0), QVariant(data), Qt.UserRole)
class ClassificationWdg(QWidget, Ui_ClassificationWdg): def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface and the events layer self.iface = iface self.vl = Utils.eventsVl() self.canvas = self.iface.mapCanvas() self._prevMapTool = None self._sharedData = {} # it will contain the classification map (data shared through the all cross sections) # create the maptool to define the area of interest self.areaDrawer = PolygonDrawer(self.iface.mapCanvas(), {'color':QColor('#333333'), 'border':2, 'enableSnap':False, 'keepAfterEnd':True}) self.areaDrawer.setAction( self.drawAreaBtn ) self.connect(self.areaDrawer, SIGNAL("geometryEmitted"), self.areaCreated) # create the maptool to create classification buffers self.segmentDrawer = SegmentDrawer(self.iface.mapCanvas(), {'color':QColor('cyan'), 'border':3, 'enableSnap':False}) self.segmentDrawer.setAction( self.addBufferBtn ) self.connect(self.segmentDrawer, SIGNAL("geometryEmitted"), self.midlineBufferCreated) # initialize the table that will contain classification buffers self.buffersTable.setModel( self.BuffersTableModel(self.buffersTable) ) self.connect( self.buffersTable.selectionModel(), SIGNAL("currentRowChanged(const QModelIndex &, const QModelIndex &)"), self.buffersSelectionChanged) self.connect( self.buffersTable.model(), SIGNAL("bufferWidthChanged"), self.updateBuffer) # connect actions to the widgets self.connect(self.drawAreaBtn, SIGNAL("clicked()"), self.drawArea) self.connect(self.clearAreaBtn, SIGNAL("clicked()"), self.clearArea) self.connect(self.addBufferBtn, SIGNAL("clicked()"), self.drawBuffer) self.connect(self.delBufferBtn, SIGNAL("clicked()"), self.deleteBuffer) self.connect(self.crossSectionBtn, SIGNAL("clicked()"), self.openCrossSection) self.connect(self.displayClassifiedDataBtn, SIGNAL("clicked()"), self.loadClassifiedData) # disable buttons self.delBufferBtn.setEnabled(False) self.crossSectionBtn.setEnabled(False) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool and prevMapTool not in (self.areaDrawer, self.segmentDrawer): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.areaDrawer.stopCapture() self.segmentDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self # clear rubberbands self.clearArea() self.clearBuffers() # restore the previous maptool self.restorePrevMapTool() # delete the maptools self.areaDrawer.deleteLater() self.areaDrawer = None self.segmentDrawer.deleteLater() self.segmentDrawer = None return QWidget.deleteLater(self, *args) def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.areaDrawer.isEmittingPoints: self.areaDrawer.reset() else: self.areaDrawer.rubberBand.show() if show else self.areaDrawer.rubberBand.hide() self.segmentDrawer.reset() model = self.buffersTable.model() for row in range(model.rowCount()): # get item data index = model.index(row, 0) buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineRb.show() if show else midlineRb.hide() bufferRb.show() if show else bufferRb.hide() if dlg: dlg.hide() def drawArea(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.areaDrawer.startCapture() def clearArea(self): # remove the displayed polygon self.areaDrawer.reset() def areaCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def drawBuffer(self): """ set the maptool to draw the buffer midline """ # store the previous maptool self.storePrevMapTool() # set the segment drawer as current maptool self.segmentDrawer.startCapture() def clearBuffers(self): """ delete all the classification buffers and related rubberbands """ self.segmentDrawer.reset() model = self.buffersTable.model() for row in range(model.rowCount()-1, -1, -1): self.deleteBuffer(row) self.buffersTable.model().clear() def midlineBufferCreated(self, line): """ called after the buffer midline is drawn by the user """ # restore the previous maptool self.restorePrevMapTool() # check for a valid segment if not line: return segment = line.asPolyline() if len(segment) != 2: return # create the midline rubber band midlineRb = QgsRubberBand(self.canvas, False) midlineRb.setColor( QColor('cyan') ) midlineRb.setWidth( 3 ) # create the buffer rubber band bufferRb = QgsRubberBand(self.canvas, True) bufferRb.setColor( QColor('fuchsia') ) bufferRb.setWidth( 1 ) # define buffer width in layer CRS unit units = self.canvas.mapUnits() if units == QGis.Meters: bufferwidth = 100000 # 100 Km elif units == QGis.Feet: bufferwidth = 300000 # 300 Kft else: bufferwidth = 1.0 # 1 degree # append the new classification buffer to the table data = [segment, (midlineRb, bufferRb), None] row = self.buffersTable.model().append( bufferwidth, data ) self.buffersTable.setCurrentIndex( self.buffersTable.model().index( row, 0 ) ) def redrawMidlineRubberBand(self, rubberBand, segment): """ re-draw the midline rubberband by segment """ rubberBand.reset(False) # add points to the midline rubber band rubberBand.addPoint( segment[0], False ) rubberBand.addPoint( segment[1], True ) rubberBand.show() def redrawBufferRubberBand(self, rubberBand, segment, width): """ re-draw the buffer rubberband by segment and width """ rubberBand.reset(True) # create a buffer around the line import math (x1,y1), (x2,y2) = segment angle = math.atan(float(y2-y1)/(x2-x1)) if x2-x1 != 0 else math.pi/2 xincr, yincr = width*math.sin(angle), width*math.cos(angle) # add the buffer geom points to the rubber band rubberBand.addPoint( QgsPoint(x1+xincr, y1-yincr), False ) rubberBand.addPoint( QgsPoint(x1-xincr, y1+yincr), False ) rubberBand.addPoint( QgsPoint(x2-xincr, y2+yincr), False ) rubberBand.addPoint( QgsPoint(x2+xincr, y2-yincr), True ) rubberBand.show() def deleteBuffer(self, row=None): """ delete the classification buffer at row and its rubberbands """ model = self.buffersTable.model() if row is None: row = self.buffersTable.currentIndex().row() if row < 0 or row >= model.rowCount(): return # get item data segment, (midlineRb, bufferRb), dlg = model.getAdditionalData( model.index(row, 0) ) # delete the item model.removeRows( row, 1 ) # destroy item data midlineRb.reset(False) bufferRb.reset(True) if dlg: self.disconnect(self, SIGNAL("classificationUpdated"), dlg.refresh) dlg.close() dlg.deleteLater() def updateBuffer(self, index): """ re-draw the rubberbands for the classification buffer at index """ model = self.buffersTable.model() buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) self.redrawBufferRubberBand(bufferRb, segment, buffersize) self.redrawMidlineRubberBand(midlineRb, segment) if dlg: dlg.refresh() def buffersSelectionChanged(self, current, previous): """ update buttons, then highlight the rubberbands for the selected classification buffer """ self.delBufferBtn.setEnabled(current.isValid()) self.crossSectionBtn.setEnabled(current.isValid()) model = self.buffersTable.model() for index in (previous, current): if not index.isValid(): continue buffersize = model.getBufferSize(index) segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineRb.setColor(QColor('#88FFFF')) if index == current else midlineRb.setColor(QColor('#00FFFF')) # color cyan bufferRb.setColor(QColor('#FF88FF')) if index == current else bufferRb.setColor(QColor('#FF00FF')) # color fuchsia self.redrawBufferRubberBand(bufferRb, segment, buffersize) self.redrawMidlineRubberBand(midlineRb, segment) def openCrossSection(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: dlg = self._openCrossSection() finally: QApplication.restoreOverrideCursor() def _openCrossSection(self): """ open a cross section displaying geometries within this classification buffer """ index = self.buffersTable.currentIndex() if not index.isValid(): return # get the geometry which defines the area of interest areaGeom = self.areaDrawer.geometry() if not areaGeom or areaGeom.isGeosEmpty(): QMessageBox.warning(self, "No Area of interest", "Define an Area of interest and then try again.") return # get midline and area geometries for the selected classification buffer model = self.buffersTable.model() segment, (midlineRb, bufferRb), dlg = model.getAdditionalData(index) midlineGeom = midlineRb.asGeometry() bufferGeom = bufferRb.asGeometry() # compute the spatial filter filteringGeom = bufferGeom.intersection( areaGeom ) if not filteringGeom or filteringGeom.isGeosEmpty(): QMessageBox.warning(self, "Invalid area", "Intersection between the Area of interest and the selected classification buffer area is invalid or empty.") return # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs() ) ret = filteringGeom.transform( toLayerCrsTransform ) if ret != 0: QMessageBox.warning(self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return # get the depth field index pr = self.vl.dataProvider() key2index = Utils.key2indexFieldMap( pr.fields() ) depthIndex = key2index['depth'] # the following x and y lists will contain respectively the distance # along the buffer and the depth, the info list instead will contain # additional data x, y, info = [], [], [] # fetch and loop through the features pr.select([ depthIndex ], filteringGeom.boundingBox(), True) toMapCrsTransform = QgsCoordinateTransform( self.vl.crs(), self.canvas.mapRenderer().destinationCrs() ) f = QgsFeature() while pr.nextFeature( f ): geom = f.geometry() # filter features by spatial filter if not filteringGeom.contains( geom ): continue # transform to map CRS if geom.transform( toMapCrsTransform ) != 0: continue geom = QgsGeometry.fromPoint(geom.asPoint()) # store distance and depth values dist = Utils.distanceAlongProfile( midlineGeom, geom ) x.append( Utils.toDisplayedSize( dist ) ) # convert to Km/Kft/degrees y.append( f.attributeMap()[ depthIndex ].toDouble()[0] * -1 ) info.append( f.id() ) if len(x) <= 0: QMessageBox.information(self, "Cross section", "No features in the result") return # plot now! if not dlg: from cross_section_wdg import CrossSectionDlg dlg = CrossSectionDlg(self._sharedData, self) self.connect(dlg, SIGNAL("classificationUpdateRequested"), self.updateClassification) self.connect(self, SIGNAL("classificationUpdated"), dlg.refresh) # store the dialog into the item data model.setAdditionalData( index, [segment, (midlineRb, bufferRb), dlg] ) dlg.setData(x, y, info) dlg.setLabels( "Distance", "Depth" ) self.updateClassification() dlg.show() def updateClassification(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: self._updateClassification() finally: QApplication.restoreOverrideCursor() def _updateClassification(self): model = self.buffersTable.model() # update the earthquakes classification self._sharedData.clear() for row in range(model.rowCount()): dlg = model.getAdditionalData(model.index(row, 0))[2] if not dlg: continue classified = (shallow, deep) = dlg.classify() # update the shared data for typeIndex, data in enumerate( classified ): for fid in data: # avoid duplicates if self._sharedData.has_key(fid): # do not update shallow earthquakes if self._sharedData[ fid ] == 0: continue # already present self._sharedData[ fid ] = typeIndex self.emit( SIGNAL("classificationUpdated") ) def loadClassifiedData(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) # store the current render flag state, then unset it prev_render_flag = self.iface.mapCanvas().renderFlag() self.iface.mapCanvas().setRenderFlag( False ) try: self._loadClassifiedData() finally: # restore render flag state and cursor self.iface.mapCanvas().setRenderFlag( prev_render_flag ) QApplication.restoreOverrideCursor() def _loadClassifiedData(self): # get the geometry which defines the area of interest areaGeom = self.areaDrawer.geometry() if not areaGeom or areaGeom.isGeosEmpty(): QMessageBox.warning(self, "No Area of interest", "Define an Area of interest and then try again.") return # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), self.vl.crs() ) ret = areaGeom.transform( toLayerCrsTransform ) if ret != 0: QMessageBox.warning(self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return # update the classification self.updateClassification() classifiedVl = Utils.classifiedVl() outpr = classifiedVl.dataProvider() # remove all the features in the classified events layer classifiedVl.removeSelection() classifiedVl.invertSelection() outpr.deleteFeatures( classifiedVl.selectedFeaturesIds() ) # fetch and loop through the features inpr = self.vl.dataProvider() inpr.select(self.vl.pendingAllAttributesList(), areaGeom.boundingBox(), True) betweenLayersCrsTransform = QgsCoordinateTransform( self.vl.crs(), classifiedVl.crs() ) f = QgsFeature() while inpr.nextFeature( f ): geom = f.geometry() # filter features by spatial filter if not areaGeom.contains( geom ): continue # skip unclassified data if f.id() not in self._sharedData: continue classType = self._sharedData[ f.id() ] # transform to classified layer CRS if geom.transform( betweenLayersCrsTransform ) != 0: continue f.setGeometry( QgsGeometry.fromPoint(geom.asPoint()) ) # set feature attributes attrs = {} for index, attr in sorted(f.attributeMap().iteritems()): attrs[ len(attrs) ] = attr attrs[ len(attrs) ] = 'shallow' if classType == 0 else 'deep' f.setAttributeMap( attrs ) # add the feature outpr.addFeatures( [f] ) # update layer's extent when new features have been added # because change of extent in provider is not propagated to the layer classifiedVl.updateExtents() # add the layer to the map Utils.addVectorLayer( classifiedVl ) class BuffersTableModel(QStandardItemModel): def __init__(self, parent=None): self.header = ["Buffer width"] QStandardItemModel.__init__(self, 0, len(self.header), parent) def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return QVariant(self.header[section]) if orientation == Qt.Vertical: return QVariant(section+1) return QVariant() def append(self, buffersize, data): item = QStandardItem() item.setFlags( item.flags() | Qt.ItemIsEditable ) self.appendRow( [item] ) row = self.rowCount()-1 # store buffersize and additional data self.blockSignals(True) self.setBufferSize(self.index(row, 0), buffersize) self.setAdditionalData(self.index(row, 0), data) self.blockSignals(False) # a new buffer was added, trigger for its repainting self.emit( SIGNAL("bufferWidthChanged"), self.index(row, 0) ) return row def setData(self, index, data, role=Qt.EditRole): ret = QStandardItemModel.setData(self, index, data, role) if role == Qt.EditRole and index.column() == 0: self.emit( SIGNAL("bufferWidthChanged"), index ) return ret def getBufferSize(self, index): if index.isValid(): displayed_size = self.data( self.index(index.row(), 0) ).toDouble()[0] # convert back to m/ft/degrees return Utils.fromDisplayedSize( displayed_size ) def setBufferSize(self, index, buffersize): if index.isValid(): # display Km/Kft/degrees displayed_size = Utils.toDisplayedSize( buffersize ) self.setData( self.index(index.row(), 0), QVariant( displayed_size ) ) def getAdditionalData(self, index): if index.isValid(): return self.data( self.index(index.row(), 0), Qt.UserRole ).toPyObject() def setAdditionalData(self, index, data): if index.isValid(): self.setData( self.index(index.row(), 0), QVariant(data), Qt.UserRole )
class ProcessingWdg(QWidget, Ui_ProcessingWdg): def __init__(self, iface, parent=None): QWidget.__init__(self, parent) self.setupUi(self) # store the iface self.iface = iface self.canvas = self.iface.mapCanvas() self._prevMapTool = None self.algorithmStacked.setCurrentIndex(0) # create the maptool to draw polygons self.polygonDrawer = PolygonDrawer( self.canvas, {'color':QColor("#666666"), 'enableSnap':False, 'keepAfterEnd':True} ) self.polygonDrawer.setAction( self.drawPolygonBtn ) self.connect(self.polygonDrawer, SIGNAL("geometryEmitted"), self.polygonCreated) # connect actions to the widgets self.connect(self.drawPolygonBtn, SIGNAL("clicked()"), self.drawPolygon) self.connect(self.clearPolygonBtn, SIGNAL("clicked()"), self.clearPolygon) QObject.connect(self.declusterWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.completenessWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.recurrenceWdg, SIGNAL("dataRequested"), self.fillData) QObject.connect(self.maxMagnitudeWdg, SIGNAL("dataRequested"), self.fillData) def storePrevMapTool(self): prevMapTool = self.canvas.mapTool() if prevMapTool not in (self.polygonDrawer,): self._prevMapTool = prevMapTool def restorePrevMapTool(self): self.polygonDrawer.stopCapture() if self._prevMapTool: self.canvas.setMapTool(self._prevMapTool) def showEvent(self, event): self.showRubberBands(True) QWidget.showEvent(self, event) def hideEvent(self, event): self.showRubberBands(False) self.restorePrevMapTool() QWidget.hideEvent(self, event) def deleteLater(self, *args): #print "deleting", self self.clearPolygon() # restore the previous maptool self.restorePrevMapTool() # delete the polygon drawer maptool self.polygonDrawer.deleteLater() self.polygonDrawer = None QWidget.deleteLater(self, *args) def showRubberBands(self, show=True): """ show/hide all the rubberbands """ if self.polygonDrawer.isEmittingPoints: self.polygonDrawer.reset() else: self.polygonDrawer.rubberBand.show() if show else self.polygonDrawer.rubberBand.hide() def drawPolygon(self): # store the previous maptool self.storePrevMapTool() # set the polygon drawer as current maptool self.polygonDrawer.startCapture() def clearPolygon(self): # remove the displayed polygon self.polygonDrawer.reset() def polygonCreated(self, polygon): # restore the previous maptool self.restorePrevMapTool() def fillData(self, data, panMap): """ fetch data from the classified layer and put them into the passed arguments: **data** list will contain the selected features attributes, **panMap** dictionary will keep the association between fields and their position in the data list """ classifiedVl = Utils.classifiedVl() pr = classifiedVl.dataProvider() # spatial filter spatialFilter = self.polygonDrawer.geometry() if not spatialFilter: extent = QgsRectangle() else: # convert the spatial filter to the layer CRS toLayerCrsTransform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), classifiedVl.crs() ) ret = spatialFilter.transform( toLayerCrsTransform ) if ret != 0: QMessageBox.warning(self, "Invalid area", "Unable to tranform the selected area to the layer CRS.") return extent = spatialFilter.boundingBox() index2key = Utils.index2keyFieldMap( pr.fields() ) # fetch and loop through the features pr = classifiedVl.dataProvider() pr.select(classifiedVl.pendingAllAttributesList(), extent, True) f = QgsFeature() while pr.nextFeature( f ): # filter features by spatial filter if spatialFilter and not spatialFilter.contains( f.geometry() ): continue # create a new row: [id, *fields] attrs = f.attributeMap() row = [ f.id() ] for index, val in sorted(attrs.iteritems()): row.append( attrs[ index ] ) # store the index of well-known field in the pan map so they # can be found easily later key = index2key.get(index, None) if key is not None: panMap[key] = len(row)-1 # append to the data buffer data.append( row ) if len(data) <= 0: QMessageBox.information(self, "Processing", "No features in the result") return