class LineDrawer(QgsMapToolEmitPoint): def __init__(self, canvas): # call the parent constructor QgsMapToolEmitPoint.__init__(self, canvas) # store the passed canvas self.canvas = canvas # flag to know whether the tool is performing a drawing operation self.isDrawing = False # create and setup the rubber band to display the line self.rubberBand = QgsRubberBand( self.canvas, False ) # False = not a polygon = a line self.rubberBand.setColor( Qt.red ) self.rubberBand.setWidth( 1 ) def clear(self): self.rubberBand.reset( False ) # False = not a polygon = a line def delete(self): self.canvas.scene().removeItem( self.rubberBand ) def canvasPressEvent(self, e): # which the mouse button? if e.button() == Qt.LeftButton: # left click # if it's the first left click, clear the rubberband if not self.isDrawing: self.clear() # we are drawing now self.isDrawing = True # convert the clicked position to map coordinates point = self.toMapCoordinates( e.pos() ) # add a new point to the rubber band self.rubberBand.addPoint( point, True ) # True = display updates on the canvas # and finally show the rubber band self.rubberBand.show() elif e.button() == Qt.RightButton: # right click, stop drawing self.isDrawing = False # emit a signal self.emit( SIGNAL("editingFinished()") ) def canvasMoveEvent(self, e): # check if it's already drawing if not self.isDrawing: return # convert the mouse position to map coordinates point = self.toMapCoordinates( e.pos() ) # move the last point to the new coordinates self.rubberBand.movePoint( point ) def geometry(self): return self.rubberBand.asGeometry()
class PlaceMarkerMapTool(QgsMapToolEmitPoint): ''' classdocs ''' def __init__(self, canvas): ''' Constructor ''' self.canvas = canvas super(PlaceMarkerMapTool, self).__init__(self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.reset() def reset(self): self.rubberBand.reset(QGis.Polygon) def canvasReleaseEvent(self, e): p1 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() - 2) p2 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() - 2) p3 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() + 2) p4 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() + 2) self.reset() self.rubberBand.addPoint(p1, False) self.rubberBand.addPoint(p2, False) self.rubberBand.addPoint(p3, False) self.rubberBand.addPoint(p4, True) self.rubberBand.show() def deactivate(self): self.reset() super(PlaceMarkerMapTool, self).deactivate()
class GetPointMapTool(QgsMapToolEmitPoint): coordCaptured = "" def __init__(self, canvas, iface, dockwidget, currentMapTool): self.canvas = canvas self.iface = iface self.currentMapTool = currentMapTool self.dockwidget = dockwidget QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Point) self.rubberBand.setColor(QColor(255,5,5)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.point = self.toMapCoordinates(e.pos()) self.isEmittingPoint = True self.showPoint(self.point) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.coordCaptured = self.pointdef() if self.coordCaptured is not None: print "Point:", self.coordCaptured self.coordCaptured = str(self.coordCaptured).strip("(") self.coordCaptured = str(self.coordCaptured).strip(")") self.dockwidget.munLineEdit.setText(self.coordCaptured) self.iface.mapCanvas().setMapTool(self.currentMapTool) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) # self.showRect(self.startPoint, self.endPoint) def showPoint(self, point): self.rubberBand.reset(QGis.Polygon) point1 = QgsPoint(point.x(), point.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.show() def pointdef(self): return QgsPoint(self.point)
class CurrentSelection(QgsRubberBand): """ Position marker for the current location in the viewer. """ class AniObject(QObject): def __init__(self, band): super(CurrentSelection.AniObject, self).__init__() self.color = QColor() @pyqtProperty(int) def alpha(self): return self.color.alpha() @alpha.setter def alpha(self, value): self.color.setAlpha(value) def __init__(self, canvas): super(CurrentSelection, self).__init__(canvas) self.outline = QgsRubberBand(canvas) self.outline.setBrushStyle(Qt.NoBrush) self.outline.setWidth(5) self.outline.setIconSize(30) self.aniobject = CurrentSelection.AniObject(self) self.anim = QPropertyAnimation(self.aniobject, "alpha") self.anim.setDuration(500) self.anim.setStartValue(50) self.anim.setEndValue(100) self.anim.valueChanged.connect(self.value_changed) def setOutlineColour(self, color): self.outline.setColor(color) def setToGeometry(self, geom, layer): super(CurrentSelection, self).setToGeometry(geom, layer) self.outline.setToGeometry(geom, layer) self.anim.stop() self.anim.start() def reset(self, geomtype=QGis.Line): super(CurrentSelection, self).reset(geomtype) self.outline.reset(geomtype) self.anim.stop() def value_changed(self, value): self.setColor(self.aniobject.color) self.update() def setColor(self, color): self.aniobject.color = color super(CurrentSelection, self).setColor(color)
class SelectPoint(QgsMapTool): select = pyqtSignal() selectionDone = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(couleur) self.rbSelect = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) return None def canvasReleaseEvent(self, e): if e.button() == Qt.LeftButton: self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) cp = self.toMapCoordinates( QPoint(e.pos().x() - 5, e.pos().y() - 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() + 5, e.pos().y() - 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() + 5, e.pos().y() + 5)) self.rbSelect.addPoint(cp) cp = self.toMapCoordinates( QPoint(e.pos().x() - 5, e.pos().y() + 5)) self.rbSelect.addPoint(cp) self.select.emit() else: self.selectionDone.emit() return None def reset(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) self.rbSelect.reset(QgsWkbTypes.PolygonGeometry) QgsMapTool.deactivate(self)
class RubberBandResultRenderer(): def __init__(self, iface, color = QColor('magenta'), size = 12): self.iface = iface self.rb = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rb.setColor(color) self.rb.setIconSize(size) self.srs_wgs84 = QgsCoordinateReferenceSystem(4326) self.transformation = QgsCoordinateTransform(self.srs_wgs84, self.srs_wgs84) def show_point(self, point, center=False): #check srs if self.need_transform(): point = self.transform_point(point) self.rb.addPoint(point) if center: self.center_to_point(point) def clear(self): self.rb.reset(QGis.Point) def need_transform(self): return self.iface.mapCanvas().mapRenderer().destinationCrs().postgisSrid() != 4326 def transform_point(self, point): dest_srs_id = self.iface.mapCanvas().mapRenderer().destinationCrs().srsid() self.transformation.setDestCRSID(dest_srs_id) try: return self.transformation.transform(point) except: print 'Error on transform!' # DEBUG! need message??? return def center_to_point(self, point): canvas = self.iface.mapCanvas() new_extent = QgsRectangle(canvas.extent()) new_extent.scale(1, point) canvas.setExtent(new_extent) canvas.refresh()
class FreehandPolygonMaptool(QgsMapTool, QObject): trigger = pyqtSignal(QgsGeometry) def __init__(self, canvas): QgsMapTool.__init__(self,canvas) self.canvas = canvas self.rb = QgsRubberBand(canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(QColor(255, 0, 0, 50)) def activate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PolygonGeometry) def canvasMoveEvent(self, ev): worldPoint = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), ev.pos().x(), ev.pos().y()) self.rb.movePoint(worldPoint) def canvasPressEvent(self, ev): if ev.button() == Qt.LeftButton: """ Add a new point to the rubber band """ worldPoint = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), ev.pos().x(), ev.pos().y()) self.rb.addPoint(worldPoint) elif ev.button() == Qt.RightButton: """ Send back the geometry to the calling class """ self.trigger.emit(self.rb.asGeometry()) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return False
def highlight(self,geometry): def processEvents(): try: qApp.processEvents() except: QApplication.processEvents() highlight = QgsRubberBand(self.canvas, geometry.type()) highlight.setColor(QColor("#36AF6C")) highlight.setFillColor(QColor("#36AF6C")) highlight.setWidth(2) highlight.setToGeometry(geometry,self.canvas.currentLayer()) processEvents() sleep(.1) highlight.hide() processEvents() sleep(.1) highlight.show() processEvents() sleep(.1) highlight.reset() processEvents()
class DrawPoint(QgsMapTool): selectionDone = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rb.setColor(couleur) self.rb.setWidth(3) def canvasReleaseEvent(self, e): if e.button() == Qt.LeftButton: self.rb.addPoint(self.toMapCoordinates(e.pos())) self.selectionDone.emit() def reset(self): self.rb.reset(QgsWkbTypes.PointGeometry) def deactivate(self): self.rb.reset(QgsWkbTypes.PointGeometry) QgsMapTool.deactivate(self)
class QgepMapToolAddFeature( QgsMapTool ): def __init__(self, iface, layer): QgsMapTool.__init__(self, iface.mapCanvas() ) self.iface = iface self.canvas = iface.mapCanvas() self.layer = layer self.rubberband = QgsRubberBand( iface.mapCanvas(), layer.geometryType() ) self.rubberband.setColor( QColor( "#ee5555" ) ) self.rubberband.setWidth( 2 ) self.tempRubberband = QgsRubberBand( iface.mapCanvas(), layer.geometryType() ) self.tempRubberband.setColor( QColor( "#ee5555" ) ) self.tempRubberband.setWidth( 2 ) self.tempRubberband.setLineStyle(Qt.DotLine) def activate(self): QgsMapTool.activate( self ) self.canvas.setCursor( QCursor( Qt.CrossCursor ) ) pass def deactivate(self): QgsMapTool.deactivate( self ) self.canvas.unsetCursor() pass def isZoomTool( self ): return False #=========================================================================== # Events #=========================================================================== def canvasMoveEvent( self, event ): self.mouseMoved( event ) def canvasReleaseEvent( self, event ): if event.button() == Qt.RightButton: self.rightClicked ( event ) else: self.leftClicked( event ) def leftClicked(self, event): mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.rubberband.addPoint( mousePos ) self.tempRubberband.reset() def rightClicked(self, event): f = QgsFeature( self.layer.pendingFields() ) f.setGeometry( self.rubberband.asGeometry() ) dlg = self.iface.getFeatureForm( self.layer, f ) dlg.setIsAddDialog(True) dlg.exec_() self.rubberband.reset() self.tempRubberband.reset() def mouseMoved(self, event): mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.tempRubberband.movePoint( mousePos )
class DrawPolygon(QgsMapTool): '''Outil de sélection par polygone, tiré de selectPlusFr''' selectionDone = pyqtSignal() move = pyqtSignal() def __init__(self, iface, couleur): canvas = iface.mapCanvas() QgsMapTool.__init__(self, canvas) self.canvas = canvas self.iface = iface self.status = 0 self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(couleur) return None def keyPressEvent(self, e): if e.matches(QKeySequence.Undo): if self.rb.numberOfVertices() > 1: self.rb.removeLastPoint() def canvasPressEvent(self, e): if e.button() == Qt.LeftButton: if self.status == 0: self.rb.reset(QgsWkbTypes.PolygonGeometry) self.status = 1 self.rb.addPoint(self.toMapCoordinates(e.pos())) else: if self.rb.numberOfVertices() > 2: self.status = 0 self.selectionDone.emit() else: self.reset() return None def canvasMoveEvent(self, e): if self.rb.numberOfVertices() > 0 and self.status == 1: self.rb.removeLastPoint(0) self.rb.addPoint(self.toMapCoordinates(e.pos())) self.move.emit() return None def reset(self): self.status = 0 self.rb.reset(True) def deactivate(self): self.rb.reset(True) QgsMapTool.deactivate(self)
class DistanceDialog(QDialog, Ui_place_distance): def __init__(self, distance, canvas): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() # this is a reference, distance observation is modified in outer class self.distance = distance self.rubber = QgsRubberBand(canvas) self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setIconSize(self.settings.value("rubberSize")) self.x.setText("%.3f" % distance.point.x()) self.y.setText("%.3f" % distance.point.y()) self.observation.setValue(distance.observation) self.precision.setValue(distance.precision) self.observation.selectAll() @pyqtSignature("on_observation_valueChanged(double)") def on_observation_valueChanged(self, v): self.distance.observation = v self.rubber.setToGeometry(self.distance.geometry(), None) @pyqtSignature("on_precision_valueChanged(double)") def on_precision_valueChanged(self, v): self.distance.precision = v def accept(self): self.rubber.reset() QDialog.accept(self) def reject(self): self.rubber.reset() QDialog.reject(self) def closeEvent(self, e): self.rubber.reset()
class SimpleIntersectionMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.mapCanvas = iface.mapCanvas() QgsMapTool.__init__(self, self.mapCanvas) self.settings = MySettings() self.rubber = QgsRubberBand(self.mapCanvas, QGis.Point) def deactivate(self): self.rubber.reset() QgsMapTool.deactivate(self) def activate(self): QgsMapTool.activate(self) self.rubber.setWidth(self.settings.value("rubberWidth")) self.rubber.setColor(self.settings.value("rubberColor")) self.checkLayer() def canvasMoveEvent(self, mouseEvent): # put the observations within tolerance in the rubber band self.rubber.reset(QGis.Point) match = self.snap_to_intersection(mouseEvent.pos()) if match.type() == QgsPointLocator.Vertex and match.layer() is None: self.rubber.addPoint(match.point()) def canvasPressEvent(self, mouseEvent): self.rubber.reset() match = self.snap_to_intersection(mouseEvent.pos()) if match.type() != QgsPointLocator.Vertex or match.layer() is not None: return layer = self.checkLayer() if layer is None: return f = QgsFeature() initFields = layer.dataProvider().fields() f.setFields(initFields) f.initAttributes(initFields.size()) f.setGeometry(QgsGeometry().fromPoint(match.point())) layer.editBuffer().addFeature(f) layer.triggerRepaint() def snap_to_intersection(self, pos): """ Temporarily override snapping config and snap to vertices and edges of any editable vector layer, to allow selection of node for editing (if snapped to edge, it would offer creation of a new vertex there). """ map_point = self.toMapCoordinates(pos) tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings()) snap_type = QgsPointLocator.Type(QgsPointLocator.Edge) snap_layers = [] for layer in self.canvas().layers(): if not isinstance(layer, QgsVectorLayer): continue snap_layers.append(QgsSnappingUtils.LayerConfig( layer, snap_type, tol, QgsTolerance.ProjectUnits)) snap_util = self.canvas().snappingUtils() old_layers = snap_util.layers() old_mode = snap_util.snapToMapMode() old_inter = snap_util.snapOnIntersections() snap_util.setLayers(snap_layers) snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) snap_util.setSnapOnIntersections(True) m = snap_util.snapToMap(map_point) snap_util.setLayers(old_layers) snap_util.setSnapToMapMode(old_mode) snap_util.setSnapOnIntersections(old_inter) return m def checkLayer(self): # check output layer is defined layerid = self.settings.value("simpleIntersectionLayer") layer = QgsMapLayerRegistry.instance().mapLayer(layerid) if not self.settings.value("simpleIntersectionWritePoint") or layer is None: self.iface.messageBar().pushMessage("Intersect It", "You must define an output layer for simple intersections", QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None if not layer.isEditable(): self.iface.messageBar().pushMessage("Intersect It", "The output layer <b>%s must be editable</b>" % layer.name(), QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None return layer
class MetaSearchDialog(QDialog, BASE_CLASS): """main dialogue""" def __init__(self, iface): """init window""" QDialog.__init__(self) self.setupUi(self) self.iface = iface self.map = iface.mapCanvas() self.settings = QgsSettings() self.catalog = None self.catalog_url = None self.catalog_username = None self.catalog_password = None self.context = StaticContext() version = self.context.metadata.get('general', 'version') self.setWindowTitle(self.tr('MetaSearch {0}').format(version)) self.rubber_band = QgsRubberBand(self.map, True) # True = a polygon self.rubber_band.setColor(QColor(255, 0, 0, 75)) self.rubber_band.setWidth(5) # form inputs self.startfrom = 0 self.maxrecords = 10 self.timeout = 10 self.constraints = [] # Servers tab self.cmbConnectionsServices.activated.connect(self.save_connection) self.cmbConnectionsSearch.activated.connect(self.save_connection) self.btnServerInfo.clicked.connect(self.connection_info) self.btnAddDefault.clicked.connect(self.add_default_connections) self.btnCapabilities.clicked.connect(self.show_xml) self.tabWidget.currentChanged.connect(self.populate_connection_list) # server management buttons self.btnNew.clicked.connect(self.add_connection) self.btnEdit.clicked.connect(self.edit_connection) self.btnDelete.clicked.connect(self.delete_connection) self.btnLoad.clicked.connect(self.load_connections) self.btnSave.clicked.connect(save_connections) # Search tab self.treeRecords.itemSelectionChanged.connect(self.record_clicked) self.treeRecords.itemDoubleClicked.connect(self.show_metadata) self.btnSearch.clicked.connect(self.search) self.leKeywords.returnPressed.connect(self.search) # prevent dialog from closing upon pressing enter self.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False) # launch help from button self.buttonBox.helpRequested.connect(self.help) self.btnCanvasBbox.setAutoDefault(False) self.btnCanvasBbox.clicked.connect(self.set_bbox_from_map) self.btnGlobalBbox.clicked.connect(self.set_bbox_global) # navigation buttons self.btnFirst.clicked.connect(self.navigate) self.btnPrev.clicked.connect(self.navigate) self.btnNext.clicked.connect(self.navigate) self.btnLast.clicked.connect(self.navigate) self.mActionAddWms.triggered.connect(self.add_to_ows) self.mActionAddWfs.triggered.connect(self.add_to_ows) self.mActionAddWcs.triggered.connect(self.add_to_ows) self.btnShowXml.clicked.connect(self.show_xml) # settings self.radioTitleAsk.clicked.connect(self.set_ows_save_title_ask) self.radioTitleNoAsk.clicked.connect(self.set_ows_save_title_no_ask) self.radioTempName.clicked.connect(self.set_ows_save_temp_name) self.manageGui() def manageGui(self): """open window""" self.tabWidget.setCurrentIndex(0) self.populate_connection_list() self.btnCapabilities.setEnabled(False) self.spnRecords.setValue( int(self.settings.value('/MetaSearch/returnRecords', 10))) key = '/MetaSearch/%s' % self.cmbConnectionsSearch.currentText() self.catalog_url = self.settings.value('%s/url' % key) self.catalog_username = self.settings.value('%s/username' % key) self.catalog_password = self.settings.value('%s/password' % key) self.set_bbox_global() self.reset_buttons() # get preferred connection save strategy from settings and set it save_strategy = self.settings.value('/MetaSearch/ows_save_strategy', 'title_ask') if save_strategy == 'temp_name': self.radioTempName.setChecked(True) elif save_strategy == 'title_no_ask': self.radioTitleNoAsk.setChecked(True) else: self.radioTitleAsk.setChecked(True) # install proxy handler if specified in QGIS settings self.install_proxy() # Servers tab def populate_connection_list(self): """populate select box with connections""" self.settings.beginGroup('/MetaSearch/') self.cmbConnectionsServices.clear() self.cmbConnectionsServices.addItems(self.settings.childGroups()) self.cmbConnectionsSearch.clear() self.cmbConnectionsSearch.addItems(self.settings.childGroups()) self.settings.endGroup() self.set_connection_list_position() if self.cmbConnectionsServices.count() == 0: # no connections - disable various buttons state_disabled = False self.btnSave.setEnabled(state_disabled) # and start with connection tab open self.tabWidget.setCurrentIndex(1) # tell the user to add services msg = self.tr('No services/connections defined. To get ' 'started with MetaSearch, create a new ' 'connection by clicking \'New\' or click ' '\'Add default services\'.') self.textMetadata.setHtml('<p><h3>%s</h3></p>' % msg) else: # connections - enable various buttons state_disabled = True self.btnServerInfo.setEnabled(state_disabled) self.btnEdit.setEnabled(state_disabled) self.btnDelete.setEnabled(state_disabled) def set_connection_list_position(self): """set the current index to the selected connection""" to_select = self.settings.value('/MetaSearch/selected') conn_count = self.cmbConnectionsServices.count() if conn_count == 0: self.btnDelete.setEnabled(False) self.btnServerInfo.setEnabled(False) self.btnEdit.setEnabled(False) # does to_select exist in cmbConnectionsServices? exists = False for i in range(conn_count): if self.cmbConnectionsServices.itemText(i) == to_select: self.cmbConnectionsServices.setCurrentIndex(i) self.cmbConnectionsSearch.setCurrentIndex(i) exists = True break # If we couldn't find the stored item, but there are some, default # to the last item (this makes some sense when deleting items as it # allows the user to repeatidly click on delete to remove a whole # lot of items) if not exists and conn_count > 0: # If to_select is null, then the selected connection wasn't found # by QgsSettings, which probably means that this is the first time # the user has used CSWClient, so default to the first in the list # of connetions. Otherwise default to the last. if not to_select: current_index = 0 else: current_index = conn_count - 1 self.cmbConnectionsServices.setCurrentIndex(current_index) self.cmbConnectionsSearch.setCurrentIndex(current_index) def save_connection(self): """save connection""" caller = self.sender().objectName() if caller == 'cmbConnectionsServices': # servers tab current_text = self.cmbConnectionsServices.currentText() elif caller == 'cmbConnectionsSearch': # search tab current_text = self.cmbConnectionsSearch.currentText() self.settings.setValue('/MetaSearch/selected', current_text) key = '/MetaSearch/%s' % current_text if caller == 'cmbConnectionsSearch': # bind to service in search tab self.catalog_url = self.settings.value('%s/url' % key) self.catalog_username = self.settings.value('%s/username' % key) self.catalog_password = self.settings.value('%s/password' % key) if caller == 'cmbConnectionsServices': # clear server metadata self.textMetadata.clear() self.btnCapabilities.setEnabled(False) def connection_info(self): """show connection info""" current_text = self.cmbConnectionsServices.currentText() key = '/MetaSearch/%s' % current_text self.catalog_url = self.settings.value('%s/url' % key) self.catalog_username = self.settings.value('%s/username' % key) self.catalog_password = self.settings.value('%s/password' % key) # connect to the server if not self._get_csw(): return QApplication.restoreOverrideCursor() if self.catalog: # display service metadata self.btnCapabilities.setEnabled(True) metadata = render_template('en', self.context, self.catalog, 'service_metadata.html') style = QgsApplication.reportStyleSheet() self.textMetadata.clear() self.textMetadata.document().setDefaultStyleSheet(style) self.textMetadata.setHtml(metadata) def add_connection(self): """add new service""" conn_new = NewConnectionDialog() conn_new.setWindowTitle(self.tr('New Catalogue service')) if conn_new.exec_() == QDialog.Accepted: # add to service list self.populate_connection_list() self.textMetadata.clear() def edit_connection(self): """modify existing connection""" current_text = self.cmbConnectionsServices.currentText() url = self.settings.value('/MetaSearch/%s/url' % current_text) conn_edit = NewConnectionDialog(current_text) conn_edit.setWindowTitle(self.tr('Edit Catalogue service')) conn_edit.leName.setText(current_text) conn_edit.leURL.setText(url) conn_edit.leUsername.setText( self.settings.value('/MetaSearch/%s/username' % current_text)) conn_edit.lePassword.setText( self.settings.value('/MetaSearch/%s/password' % current_text)) if conn_edit.exec_() == QDialog.Accepted: # update service list self.populate_connection_list() def delete_connection(self): """delete connection""" current_text = self.cmbConnectionsServices.currentText() key = '/MetaSearch/%s' % current_text msg = self.tr('Remove service {0}?').format(current_text) result = QMessageBox.information(self, self.tr('Confirm delete'), msg, QMessageBox.Ok | QMessageBox.Cancel) if result == QMessageBox.Ok: # remove service from list self.settings.remove(key) index_to_delete = self.cmbConnectionsServices.currentIndex() self.cmbConnectionsServices.removeItem(index_to_delete) self.cmbConnectionsSearch.removeItem(index_to_delete) self.set_connection_list_position() def load_connections(self): """load services from list""" ManageConnectionsDialog(1).exec_() self.populate_connection_list() def add_default_connections(self): """add default connections""" filename = os.path.join(self.context.ppath, 'resources', 'connections-default.xml') doc = get_connections_from_file(self, filename) if doc is None: return self.settings.beginGroup('/MetaSearch/') keys = self.settings.childGroups() self.settings.endGroup() for server in doc.findall('csw'): name = server.attrib.get('name') # check for duplicates if name in keys: msg = self.tr('{0} exists. Overwrite?').format(name) res = QMessageBox.warning(self, self.tr('Loading connections'), msg, QMessageBox.Yes | QMessageBox.No) if res != QMessageBox.Yes: continue # no dups detected or overwrite is allowed key = '/MetaSearch/%s' % name self.settings.setValue('%s/url' % key, server.attrib.get('url')) self.populate_connection_list() # Settings tab def set_ows_save_title_ask(self): """save ows save strategy as save ows title, ask if duplicate""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_ask') def set_ows_save_title_no_ask(self): """save ows save strategy as save ows title, do NOT ask if duplicate""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_no_ask') def set_ows_save_temp_name(self): """save ows save strategy as save with a temporary name""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'temp_name') # Search tab def set_bbox_from_map(self): """set bounding box from map extent""" crs = self.map.mapSettings().destinationCrs() crsid = int(crs.authid().split(':')[1]) extent = self.map.extent() if crsid != 4326: # reproject to EPSG:4326 src = QgsCoordinateReferenceSystem(crsid) dest = QgsCoordinateReferenceSystem(4326) xform = QgsCoordinateTransform(src, dest) minxy = xform.transform( QgsPoint(extent.xMinimum(), extent.yMinimum())) maxxy = xform.transform( QgsPoint(extent.xMaximum(), extent.yMaximum())) minx, miny = minxy maxx, maxy = maxxy else: # 4326 minx = extent.xMinimum() miny = extent.yMinimum() maxx = extent.xMaximum() maxy = extent.yMaximum() self.leNorth.setText(str(maxy)[0:9]) self.leSouth.setText(str(miny)[0:9]) self.leWest.setText(str(minx)[0:9]) self.leEast.setText(str(maxx)[0:9]) def set_bbox_global(self): """set global bounding box""" self.leNorth.setText('90') self.leSouth.setText('-90') self.leWest.setText('-180') self.leEast.setText('180') def search(self): """execute search""" self.catalog = None self.constraints = [] # clear all fields and disable buttons self.lblResults.clear() self.treeRecords.clear() self.reset_buttons() # save some settings self.settings.setValue('/MetaSearch/returnRecords', self.spnRecords.cleanText()) # set current catalogue current_text = self.cmbConnectionsSearch.currentText() key = '/MetaSearch/%s' % current_text self.catalog_url = self.settings.value('%s/url' % key) self.catalog_username = self.settings.value('%s/username' % key) self.catalog_password = self.settings.value('%s/password' % key) # start position and number of records to return self.startfrom = 0 self.maxrecords = self.spnRecords.value() # set timeout self.timeout = self.spnTimeout.value() # bbox minx = self.leWest.text() miny = self.leSouth.text() maxx = self.leEast.text() maxy = self.leNorth.text() bbox = [minx, miny, maxx, maxy] # only apply spatial filter if bbox is not global # even for a global bbox, if a spatial filter is applied, then # the CSW server will skip records without a bbox if bbox != ['-180', '-90', '180', '90']: self.constraints.append(BBox(bbox)) # keywords if self.leKeywords.text(): # TODO: handle multiple word searches keywords = self.leKeywords.text() self.constraints.append(PropertyIsLike('csw:AnyText', keywords)) if len(self.constraints) > 1: # exclusive search (a && b) self.constraints = [self.constraints] # build request if not self._get_csw(): return # TODO: allow users to select resources types # to find ('service', 'dataset', etc.) try: self.catalog.getrecords2(constraints=self.constraints, maxrecords=self.maxrecords, esn='full') except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Search error'), self.tr('Search error: {0}').format(err)) return except Exception as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Connection error'), self.tr('Connection error: {0}').format(err)) return if self.catalog.results['matches'] == 0: QApplication.restoreOverrideCursor() self.lblResults.setText(self.tr('0 results')) return QApplication.restoreOverrideCursor() self.display_results() def display_results(self): """display search results""" self.treeRecords.clear() position = self.catalog.results['returned'] + self.startfrom msg = self.tr('Showing {0} - {1} of %n result(s)', 'number of results', self.catalog.results['matches']).format( self.startfrom + 1, position) self.lblResults.setText(msg) for rec in self.catalog.records: item = QTreeWidgetItem(self.treeRecords) if self.catalog.records[rec].type: item.setText(0, normalize_text(self.catalog.records[rec].type)) else: item.setText(0, 'unknown') if self.catalog.records[rec].title: item.setText(1, normalize_text(self.catalog.records[rec].title)) if self.catalog.records[rec].identifier: set_item_data(item, 'identifier', self.catalog.records[rec].identifier) self.btnShowXml.setEnabled(True) if self.catalog.results["matches"] < self.maxrecords: disabled = False else: disabled = True self.btnFirst.setEnabled(disabled) self.btnPrev.setEnabled(disabled) self.btnNext.setEnabled(disabled) self.btnLast.setEnabled(disabled) def record_clicked(self): """record clicked signal""" # disable only service buttons self.reset_buttons(True, False, False) if not self.treeRecords.selectedItems(): return item = self.treeRecords.currentItem() if not item: return identifier = get_item_data(item, 'identifier') try: record = self.catalog.records[identifier] except KeyError as err: QMessageBox.warning(self, self.tr('Record parsing error'), 'Unable to locate record identifier') return # if the record has a bbox, show a footprint on the map if record.bbox is not None: points = bbox_to_polygon(record.bbox) if points is not None: src = QgsCoordinateReferenceSystem(4326) dst = self.map.mapSettings().destinationCrs() geom = QgsGeometry.fromPolygon(points) if src.postgisSrid() != dst.postgisSrid(): ctr = QgsCoordinateTransform(src, dst) try: geom.transform(ctr) except Exception as err: QMessageBox.warning( self, self.tr('Coordinate Transformation Error'), str(err)) self.rubber_band.setToGeometry(geom, None) # figure out if the data is interactive and can be operated on self.find_services(record, item) def find_services(self, record, item): """scan record for WMS/WMTS|WFS|WCS endpoints""" links = record.uris + record.references services = {} for link in links: if 'scheme' in link: link_type = link['scheme'] elif 'protocol' in link: link_type = link['protocol'] else: link_type = None if link_type is not None: link_type = link_type.upper() wmswmst_link_types = list( map(str.upper, link_types.WMSWMST_LINK_TYPES)) wfs_link_types = list(map(str.upper, link_types.WFS_LINK_TYPES)) wcs_link_types = list(map(str.upper, link_types.WCS_LINK_TYPES)) # if the link type exists, and it is one of the acceptable # interactive link types, then set if all([ link_type is not None, link_type in wmswmst_link_types + wfs_link_types + wcs_link_types ]): if link_type in wmswmst_link_types: services['wms'] = link['url'] self.mActionAddWms.setEnabled(True) if link_type in wfs_link_types: services['wfs'] = link['url'] self.mActionAddWfs.setEnabled(True) if link_type in wcs_link_types: services['wcs'] = link['url'] self.mActionAddWcs.setEnabled(True) self.tbAddData.setEnabled(True) set_item_data(item, 'link', json.dumps(services)) def navigate(self): """manage navigation / paging""" caller = self.sender().objectName() if caller == 'btnFirst': self.startfrom = 0 elif caller == 'btnLast': self.startfrom = self.catalog.results['matches'] - self.maxrecords elif caller == 'btnNext': self.startfrom += self.maxrecords if self.startfrom >= self.catalog.results["matches"]: msg = self.tr('End of results. Go to start?') res = QMessageBox.information( self, self.tr('Navigation'), msg, (QMessageBox.Ok | QMessageBox.Cancel)) if res == QMessageBox.Ok: self.startfrom = 0 else: return elif caller == "btnPrev": self.startfrom -= self.maxrecords if self.startfrom <= 0: msg = self.tr('Start of results. Go to end?') res = QMessageBox.information( self, self.tr('Navigation'), msg, (QMessageBox.Ok | QMessageBox.Cancel)) if res == QMessageBox.Ok: self.startfrom = (self.catalog.results['matches'] - self.maxrecords) else: return QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: self.catalog.getrecords2(constraints=self.constraints, maxrecords=self.maxrecords, startposition=self.startfrom, esn='full') except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Search error'), self.tr('Search error: {0}').format(err)) return except Exception as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Connection error'), self.tr('Connection error: {0}').format(err)) return QApplication.restoreOverrideCursor() self.display_results() def add_to_ows(self): """add to OWS provider connection list""" conn_name_matches = [] item = self.treeRecords.currentItem() if not item: return item_data = json.loads(get_item_data(item, 'link')) caller = self.sender().objectName() # stype = human name,/Qgis/connections-%s,providername if caller == 'mActionAddWms': stype = ['OGC:WMS/OGC:WMTS', 'wms', 'wms'] data_url = item_data['wms'] elif caller == 'mActionAddWfs': stype = ['OGC:WFS', 'wfs', 'WFS'] data_url = item_data['wfs'] elif caller == 'mActionAddWcs': stype = ['OGC:WCS', 'wcs', 'wcs'] data_url = item_data['wcs'] QApplication.restoreOverrideCursor() sname = '%s from MetaSearch' % stype[1] # store connection # check if there is a connection with same name self.settings.beginGroup('/Qgis/connections-%s' % stype[1]) keys = self.settings.childGroups() self.settings.endGroup() for key in keys: if key.startswith(sname): conn_name_matches.append(key) if conn_name_matches: sname = conn_name_matches[-1] # check for duplicates if sname in keys: # duplicate found if self.radioTitleAsk.isChecked(): # ask to overwrite msg = self.tr('Connection {0} exists. Overwrite?').format( sname) res = QMessageBox.warning(self, self.tr('Saving server'), msg, QMessageBox.Yes | QMessageBox.No) if res != QMessageBox.Yes: # assign new name with serial sname = serialize_string(sname) elif self.radioTitleNoAsk.isChecked(): # don't ask to overwrite pass elif self.radioTempName.isChecked(): # use temp name sname = serialize_string(sname) # no dups detected or overwrite is allowed self.settings.beginGroup('/Qgis/connections-%s' % stype[1]) self.settings.setValue('/%s/url' % sname, clean_ows_url(data_url)) self.settings.endGroup() # open provider window ows_provider = QgsProviderRegistry.instance().selectWidget( stype[2], self) service_type = stype[0] # connect dialog signals to iface slots if service_type == 'OGC:WMS/OGC:WMTS': ows_provider.addRasterLayer.connect(self.iface.addRasterLayer) conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections') connect = 'on_btnConnect_clicked' elif service_type == 'OGC:WFS': ows_provider.addWfsLayer.connect( self.iface.mainWindow().addWfsLayer) conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections') connect = 'connectToServer' elif service_type == 'OGC:WCS': ows_provider.addRasterLayer.connect(self.iface.addRasterLayer) conn_cmb = ows_provider.findChild(QWidget, 'mConnectionsComboBox') connect = 'on_mConnectButton_clicked' ows_provider.setModal(False) ows_provider.show() # open provider dialogue against added OWS index = conn_cmb.findText(sname) if index > -1: conn_cmb.setCurrentIndex(index) # only for wfs if service_type == 'OGC:WFS': ows_provider.on_cmbConnections_activated(index) getattr(ows_provider, connect)() def show_metadata(self): """show record metadata""" if not self.treeRecords.selectedItems(): return item = self.treeRecords.currentItem() if not item: return identifier = get_item_data(item, 'identifier') try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) cat = CatalogueServiceWeb(self.catalog_url, timeout=self.timeout, username=self.catalog_username, password=self.catalog_password) cat.getrecordbyid([self.catalog.records[identifier].identifier]) except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning( self, self.tr('GetRecords error'), self.tr('Error getting response: {0}').format(err)) return except KeyError as err: QMessageBox.warning(self, self.tr('Record parsing error'), self.tr('Unable to locate record identifier')) QApplication.restoreOverrideCursor() return QApplication.restoreOverrideCursor() record = cat.records[identifier] record.xml_url = cat.request crd = RecordDialog() metadata = render_template('en', self.context, record, 'record_metadata_dc.html') style = QgsApplication.reportStyleSheet() crd.textMetadata.document().setDefaultStyleSheet(style) crd.textMetadata.setHtml(metadata) crd.exec_() def show_xml(self): """show XML request / response""" crd = XMLDialog() request_html = highlight_xml(self.context, self.catalog.request) response_html = highlight_xml(self.context, self.catalog.response) style = QgsApplication.reportStyleSheet() crd.txtbrXMLRequest.clear() crd.txtbrXMLResponse.clear() crd.txtbrXMLRequest.document().setDefaultStyleSheet(style) crd.txtbrXMLResponse.document().setDefaultStyleSheet(style) crd.txtbrXMLRequest.setHtml(request_html) crd.txtbrXMLResponse.setHtml(response_html) crd.exec_() def reset_buttons(self, services=True, xml=True, navigation=True): """Convenience function to disable WMS/WMTS|WFS|WCS buttons""" if services: self.tbAddData.setEnabled(False) self.mActionAddWms.setEnabled(False) self.mActionAddWfs.setEnabled(False) self.mActionAddWcs.setEnabled(False) if xml: self.btnShowXml.setEnabled(False) if navigation: self.btnFirst.setEnabled(False) self.btnPrev.setEnabled(False) self.btnNext.setEnabled(False) self.btnLast.setEnabled(False) def help(self): """launch help""" open_url(get_help_url()) def reject(self): """back out of dialogue""" QDialog.reject(self) self.rubber_band.reset() def _get_csw(self): """convenience function to init owslib.csw.CatalogueServiceWeb""" # connect to the server try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.catalog = CatalogueServiceWeb(self.catalog_url, timeout=self.timeout, username=self.catalog_username, password=self.catalog_password) return True except ExceptionReport as err: msg = self.tr('Error connecting to service: {0}').format(err) except ValueError as err: msg = self.tr('Value Error: {0}').format(err) except Exception as err: msg = self.tr('Unknown Error: {0}').format(err) QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('CSW Connection error'), msg) return False def install_proxy(self): """set proxy if one is set in QGIS network settings""" # initially support HTTP for now if self.settings.value('/proxy/proxyEnabled') == 'true': if self.settings.value('/proxy/proxyType') == 'HttpProxy': ptype = 'http' else: return user = self.settings.value('/proxy/proxyUser') password = self.settings.value('/proxy/proxyPassword') host = self.settings.value('/proxy/proxyHost') port = self.settings.value('/proxy/proxyPort') proxy_up = '' proxy_port = '' if all([user != '', password != '']): proxy_up = '%s:%s@' % (user, password) if port != '': proxy_port = ':%s' % port conn = '%s://%s%s%s' % (ptype, proxy_up, host, proxy_port) install_opener(build_opener(ProxyHandler({ptype: conn})))
class SimpleIntersectionMapTool(QgsMapTool): def __init__(self, iface): self.iface = iface self.mapCanvas = iface.mapCanvas() QgsMapTool.__init__(self, self.mapCanvas) self.settings = MySettings() self.rubber = QgsRubberBand(self.mapCanvas) def deactivate(self): self.rubber.reset() self.mapCanvas.layersChanged.disconnect(self.updateSnapperList) self.mapCanvas.scaleChanged.disconnect(self.updateSnapperList) QgsMapTool.deactivate(self) def activate(self): QgsMapTool.activate(self) self.rubber.setWidth(self.settings.value("rubberWidth")) self.rubber.setColor(self.settings.value("rubberColor")) self.updateSnapperList() self.mapCanvas.layersChanged.connect(self.updateSnapperList) self.mapCanvas.scaleChanged.connect(self.updateSnapperList) self.checkLayer() def updateSnapperList(self, dummy=None): # make a snapper list of all line and polygons layers self.snapperList = [] scale = self.iface.mapCanvas().mapRenderer().scale() for layer in self.mapCanvas.layers(): if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType(): if layer.geometryType() in (QGis.Line, QGis.Polygon): if not layer.hasScaleBasedVisibility() or layer.minimumScale() < scale <= layer.maximumScale(): snapLayer = QgsSnapper.SnapLayer() snapLayer.mLayer = layer snapLayer.mSnapTo = QgsSnapper.SnapToVertexAndSegment snapLayer.mTolerance = self.settings.value("selectTolerance") if self.settings.value("selectUnits") == "map": snapLayer.mUnitType = QgsTolerance.MapUnits else: snapLayer.mUnitType = QgsTolerance.Pixels self.snapperList.append(snapLayer) def canvasMoveEvent(self, mouseEvent): # put the observations within tolerance in the rubber band self.rubber.reset() for f in self.getFeatures(mouseEvent.pos()): self.rubber.addGeometry(f.geometry(), None) def canvasPressEvent(self, mouseEvent): self.rubber.reset() pos = mouseEvent.pos() features = self.getFeatures(pos) nFeat = len(features) if nFeat < 2: layerNames = " , ".join([feature.layer.name() for feature in features]) self.iface.messageBar().pushMessage("Intersect It", "You need 2 features to proceed a simple intersection." " %u given (%s)" % (nFeat, layerNames), QgsMessageBar.WARNING, 3) return intersectionP = self.intersection(features, pos) if intersectionP == QgsPoint(0,0): self.iface.messageBar().pushMessage("Intersect It", "Objects do not intersect.", QgsMessageBar.WARNING, 2) return layer = self.checkLayer() if layer is None: return f = QgsFeature() initFields = layer.dataProvider().fields() f.setFields(initFields) f.initAttributes(initFields.size()) f.setGeometry(QgsGeometry().fromPoint(intersectionP)) layer.editBuffer().addFeature(f) layer.triggerRepaint() def getFeatures(self, pixPoint): # do the snapping snapper = QgsSnapper(self.mapCanvas.mapRenderer()) snapper.setSnapLayers(self.snapperList) snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances) ok, snappingResults = snapper.snapPoint(pixPoint, []) # output snapped features features = [] alreadyGot = [] for result in snappingResults: featureId = result.snappedAtGeometry f = QgsFeature() if (result.layer.id(), featureId) not in alreadyGot: if result.layer.getFeatures(QgsFeatureRequest().setFilterFid(featureId)).nextFeature(f) is False: continue if not isFeatureRendered(self.mapCanvas, result.layer, f): continue features.append(QgsFeature(f)) features[-1].layer = result.layer alreadyGot.append((result.layer.id(), featureId)) return features def intersection(self, features, pos): # try all the combinations nFeat = len(features) intersections = [] for i in range(nFeat-1): for j in range(i+1,nFeat): intersection = features[i].geometry().intersection(features[j].geometry()) intersectionMP = intersection.asMultiPoint() intersectionP = intersection.asPoint() if len(intersectionMP) == 0: intersectionMP = intersection.asPolyline() if len(intersectionMP) == 0 and intersectionP == QgsPoint(0, 0): continue if len(intersectionMP) > 1: mousePoint = self.toMapCoordinates(pos) intersectionP = intersectionMP[0] for point in intersectionMP[1:]: if mousePoint.sqrDist(point) < mousePoint.sqrDist(intersectionP): intersectionP = QgsPoint(point.x(), point.y()) if intersectionP != QgsPoint(0,0): intersections.append(intersectionP) if len(intersections) == 0: return QgsPoint(0,0) intersectionP = intersections[0] for point in intersections[1:]: if mousePoint.sqrDist(point) < mousePoint.sqrDist(intersectionP): intersectionP = QgsPoint(point.x(), point.y()) return intersectionP def checkLayer(self): # check output layer is defined layerid = self.settings.value("simpleIntersectionLayer") layer = QgsMapLayerRegistry.instance().mapLayer(layerid) if not self.settings.value("simpleIntersectionWritePoint") or layer is None: self.iface.messageBar().pushMessage("Intersect It", "You must define an output layer for simple intersections", QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None if not layer.isEditable(): self.iface.messageBar().pushMessage("Intersect It", "The output layer <b>%s must be editable</b>" % layer.name(), QgsMessageBar.WARNING, 3) self.mapCanvas.unsetMapTool(self) return None return layer
class GeorefRasterBy2PointsMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.firstPoint = None self.rubberBandOrigin = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubberBandOrigin.setColor(Qt.red) self.rubberBandOrigin.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubberBandOrigin.setIconSize(7) self.rubberBandOrigin.setWidth(2) self.rubberBandDisplacement = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(2) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = self.firstPoint = None self.isEmittingPoint = False self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def deactivate(self): QgsMapToolEmitPoint.deactivate(self) self.reset() def canvasPressEvent(self, e): if self.firstPoint is None: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = \ self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({ "action": "2pointsA", "center": self.layer.center }) else: self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) self.layer.history.append({ "action": "2pointsB", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale, "rotation": None }) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() if self.firstPoint is None: x = (self.originalCenter.x() + self.endPoint.x() - self.startPoint.x()) y = (self.originalCenter.y() + self.endPoint.y() - self.startPoint.y()) self.layer.setCenter(QgsPointXY(x, y)) self.firstPoint = self.endPoint setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() else: rotation = self.computeRotation() xScale = yScale = self.computeScale() self.layer.moveCenterFromPointRotate(self.firstPoint, rotation, xScale, yScale) self.layer.setScale(self.layer.xScale * xScale, self.layer.yScale * yScale) val = self.layer.rotation + rotation if val > 180: val = val - 360 self.iface.mainWindow().findChild( QDoubleSpinBox, 'FreehandRasterGeoreferencer_spinbox').setValue(val) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rasterShadow.reset() self.firstPoint = None self.startPoint = self.endPoint = None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) if self.firstPoint is None: self.showDisplacement(self.startPoint, self.endPoint) else: self.endY = e.pos().y() rotation = self.computeRotation() xScale = yScale = self.computeScale() self.showRotationScale(rotation, xScale, yScale) def computeRotation(self): # The angle is the difference between angle # horizontal/endPoint-firstPoint and horizontal/startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.degrees(math.atan2(-dY, dX) - math.atan2(-dY0, dX0)) def computeScale(self): # The scale is the ratio between endPoint-firstPoint and # startPoint-firstPoint. dX0 = self.startPoint.x() - self.firstPoint.x() dY0 = self.startPoint.y() - self.firstPoint.y() dX = self.endPoint.x() - self.firstPoint.x() dY = self.endPoint.y() - self.firstPoint.y() return math.sqrt((dX * dX + dY * dY) / (dX0 * dX0 + dY0 * dY0)) def showRotationScale(self, rotation, xScale, yScale): center, _, _, _ = self.layer.transformParameters() # newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.firstPoint, rotation, xScale, yScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # Calculate the displacement of the center due to the rotation from # another point. newCenterDX = (cornerPoints[0].x() + cornerPoints[2].x()) / 2 - center.x() newCenterDY = (cornerPoints[0].y() + cornerPoints[2].y()) / 2 - center.y() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement(newCenterDX, newCenterDY, False) self.rasterShadow.setDeltaScale(xScale, yScale, False) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point1 = QgsPointXY(self.firstPoint.x(), self.firstPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() def showDisplacement(self, startPoint, endPoint): self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry) self.rubberBandOrigin.addPoint(endPoint, True) self.rubberBandOrigin.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint(self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement( self.endPoint.x() - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class ShapeTool(QgsMapTool): #signal emitted when the mouse is clicked. This indicates that the tool finished its job toolFinished = pyqtSignal() def __init__(self, canvas, geometryType, param, type, color = QColor( 254, 178, 76, 63 )): """ Constructor """ QgsMapTool.__init__(self, canvas) self.canvas = canvas self.active = False self.geometryType = self.tr(geometryType) self.param=param self.type=type self.cursor=None self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.setColor(color) self.reset() self.rotAngle = 0 self.currentCentroid = None self.rotate = False def setColor(self, mFillColor): """ Adjusting the color to create the rubber band """ self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(1) def reset(self): """ Resetting the rubber band """ self.startPoint = self.endPoint = None self.isEmittingPoint = False try: self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) except: pass def rotateRect(self, centroid, e): """ Calculates the angle for the rotation. """ item_position = self.canvas.mapToGlobal(e.pos()) c = self.toCanvasCoordinates(centroid) c = self.canvas.mapToGlobal(c) rotAngle = pi - atan2( item_position.y() - c.y(), item_position.x() - c.x()) return rotAngle def canvasPressEvent(self, e): """ When the canvas is pressed the tool finishes its job """ # enforce mouse restoring if clicked right after rotation QApplication.restoreOverrideCursor() self.canvas.unsetMapTool(self) self.toolFinished.emit() def _baseDistanceInMeters(self): """ Calculates the distance in meters of 2 points 1 unit map away on current canvas CRS. :return: (float) distance in meters between two points 1 map unit apart from each other. """ source_crs = self.canvas.mapSettings().destinationCrs() dest_crs = QgsCoordinateReferenceSystem(3857) tr = QgsCoordinateTransform( source_crs, dest_crs, QgsCoordinateTransformContext()) p1t = QgsGeometry().fromPointXY(QgsPointXY(1, 0)) p1t.transform(tr) p2t = QgsGeometry().fromPointXY(QgsPointXY(0, 0)) p2t.transform(tr) return QgsDistanceArea().measureLine(p1t.asPoint(), p2t.asPoint()) def getAdjustedSize(self, size): """ If map unit is not metric, the figure to be drawn needs to have its size adjusted. This is necessary because input parameters are designed to be meters on tool's GUI. :param size: (float) tool's radius/length reference size in meters. :return: (float) """ source_crs = self.canvas.mapSettings().destinationCrs() if source_crs.mapUnits() != QgsUnitTypes.DistanceMeters: return size / self._baseDistanceInMeters() return size def canvasMoveEvent(self, e): """ Deals with mouse move event to update the rubber band position in the canvas """ ctrlIsHeld = QApplication.keyboardModifiers() == Qt2.ControlModifier if e.button() != None and not ctrlIsHeld: if self.rotate: # change rotate status self.rotate = False QApplication.restoreOverrideCursor() self.endPoint = self.toMapCoordinates( e.pos() ) elif e.button() != None and ctrlIsHeld \ and self.geometryType == self.tr(u"Square"): # calculate angle between mouse and last rubberband centroid before holding control self.rotAngle = self.rotateRect(self.currentCentroid, e) if not self.rotate: # only override mouse if it is not overriden already QApplication.setOverrideCursor(QCursor(Qt2.BlankCursor)) self.rotate = True if self.geometryType == self.tr(u"Circle"): self.showCircle(self.endPoint) elif self.geometryType == self.tr(u"Square"): self.showRect(self.endPoint, sqrt(self.param)/2, self.rotAngle) def showCircle(self, startPoint): """ Draws a circle in the canvas """ nPoints = 50 x = startPoint.x() y = startPoint.y() if self.type == self.tr('distance'): r = self.getAdjustedSize(self.param) self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) for itheta in range(nPoints+1): theta = itheta*(2.0*pi/nPoints) self.rubberBand.addPoint(QgsPointXY(x+r*cos(theta), y+r*sin(theta))) self.rubberBand.show() else: r = self.getAdjustedSize(sqrt(self.param/pi)) self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) for itheta in range(nPoints+1): theta = itheta*(2.0*pi/nPoints) self.rubberBand.addPoint(QgsPointXY(x+r*cos(theta), y+r*sin(theta))) self.rubberBand.show() def showRect(self, startPoint, param, rotAngle=0): """ Draws a rectangle in the canvas """ self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) x = startPoint.x() # center point x y = startPoint.y() # center point y # rotation angle is always applied in reference to center point # to avoid unnecessary calculations c = cos(rotAngle) s = sin(rotAngle) # translating coordinate system to rubberband centroid param = self.getAdjustedSize(param) for posx, posy in ((-1, -1), (-1, 1), (1, 1), (1, -1)): px = posx * param py = posy * param pnt = QgsPointXY(px * c - py * s + x, py * c + px * s + y) self.rubberBand.addPoint(pnt, False) self.rubberBand.setVisible(True) self.rubberBand.updateRect() self.rubberBand.update() self.rubberBand.show() self.currentCentroid = startPoint def deactivate(self): """ Deactivates the tool and hides the rubber band """ self.rubberBand.hide() QgsMapTool.deactivate(self) # restore mouse in case tool is disabled right after rotation QApplication.restoreOverrideCursor() def activate(self): """ Activates the tool """ QgsMapTool.activate(self)
class RectangleMapTool(QgsMapToolEmitPoint): boxCreated = pyqtSignal(QgsRectangle) def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) #self.rubberBand.setBorderColor(Qt.red) self.rubberBand.setBrushStyle(Qt.Dense6Pattern) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False r = self.rectangle() if r is not None: self.boxCreated.emit(r) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint)
class InfoTool(TouchMapTool): infoResults = pyqtSignal(dict) def __init__(self, canvas, snapradius = 2): super(InfoTool, self).__init__(canvas) self.radius = snapradius self.band = QgsRubberBand(self.canvas) self.band.setColor(QColor.fromRgb(224,162,16)) self.band.setWidth(3) self.selectband = None self.selectrect = QRect() self.dragging = False self.selectionlayers = [] def getFeatures(self, rect, firstonly=False): self.band.reset() for layer in self.selectionlayers.itervalues(): if (not layer.type() == QgsMapLayer.VectorLayer or layer.geometryType() == QGis.NoGeometry): continue rect = self.toLayerCoordinates(layer, rect) rq = QgsFeatureRequest().setFilterRect(rect)\ .setFlags(QgsFeatureRequest.ExactIntersect)\ .setSubsetOfAttributes([]) features = [] if firstonly: try: feature = layer.getFeatures(rq).next() if feature.isValid(): features.append(feature) except StopIteration: continue else: for feature in layer.getFeatures(rq): if feature.isValid(): features.append(feature) yield layer, features def toSearchRect(self, point): point = self.toMapCoordinates(point) rect = QgsRectangle(point.x(), point.y(), point.x() + 10, point.y() + 10) return rect def canvasPressEvent(self, event): if self.pinching: return self.dragging = False self.selectrect.setRect(0, 0, 0, 0) self.selectband = QgsRubberBand(self.canvas, QGis.Polygon ) self.selectband.setColor(QColor.fromRgb(0,0,255, 65)) self.selectband.setWidth(5) def canvasMoveEvent(self, event): if self.pinching: return if not event.buttons() == Qt.LeftButton: return if not self.dragging: self.selectrect.setTopLeft(event.pos()) self.dragging = True self.selectrect.setBottomRight(event.pos()) maptoolutils.setRubberBand(self.canvas, self.selectrect, self.selectband) def canvasReleaseEvent(self, event): if self.pinching: return if self.dragging: geometry = self.selectband.asGeometry() if not geometry: return rect = geometry.boundingBox() firstonly = False else: firstonly = True rect = self.toSearchRect(event.pos()) self.dragging = False self.selectband.reset() results = OrderedDict((l,f) for l, f in self.getFeatures(rect)) self.infoResults.emit(results)
class FinderBox(QComboBox): running = False to_finish = 0 search_started = pyqtSignal() search_finished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.marker = None self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.result_view = QTreeView() self.result_view.setHeaderHidden(True) self.result_view.setMinimumHeight(300) self.result_view.activated.connect(self.itemActivated) self.result_view.pressed.connect(self.itemPressed) self.setView(self.result_view) self.result_model = ResultModel(self) self.setModel(self.result_model) self.finders = finders for finder in list(self.finders.values()): finder.result_found.connect(self.result_found) finder.limit_reached.connect(self.limit_reached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon( QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) button_size = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = button_size.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber #clear marker that the lonlat seted def clearMarker(self): self.mapCanvas.scene().removeItem(self.marker) self.marker = None def clearSelection(self): self.result_model.setSelected(None, self.result_view.palette()) self.rubber.reset() #self.clearMarker() def clear(self): self.clearSelection() self.result_model.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() self.clearMarker() QComboBox.keyPressEvent(self, event) def lnglatFinder(self, to_find): import re m = re.match(r'(?P<lon>-?\d*(.\d+))\s+(?P<lat>-?\d*(.\d+))', to_find) if not m: return False x = float(m.group('lon')) y = float(m.group('lat')) return self.zoomLnglat(x, y) def zoomLnglat(self, lng, lat): x, y = lng, lat canvas = self.mapCanvas currExt = canvas.extent() canvasCenter = currExt.center() dx = float(x) - canvasCenter.x() dy = float(y) - canvasCenter.y() xMin = currExt.xMinimum() + dx xMax = currExt.xMaximum() + dx yMin = currExt.yMinimum() + dy yMax = currExt.yMaximum() + dy rect = QgsRectangle(xMin, yMin, xMax, yMax) canvas.setExtent(rect) pt = QgsPointXY(float(x), float(y)) self.marker = QgsVertexMarker(canvas) self.marker.setCenter(pt) self.marker.setIconSize(18) self.marker.setPenWidth(2) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) canvas.refresh() return True # def geocodeFinder(self, to_finder): # print(to_finder[:2]) # if not to_finder[:2] == 'b:': # return False # # address = to_finder[2:] # url = MySettings().value("baiduUrl") # url = url + parse.quote(address) # # response = request.urlopen(url) # content = response.read() # data = json.loads(content) # print(data) # lng, lat = (data['result']['location']['lng'], data['result']['location']['lat']) # from .cood_trans import bd09_to_wgs84 # lng, lat = bd09_to_wgs84(lng, lat) # print(f'{lng}-{lat}') # return self.zoomLnglat(lng, lat) def search(self): # self.geocode() if self.running: return to_find = self.lineEdit().text() if not to_find or to_find == '': return # if not (self.lnglatFinder(to_find) or self.geocodeFinder(to_find)): if not self.lnglatFinder(to_find): self.showPopup() self.running = True self.search_started.emit() self.clearSelection() self.result_model.clearResults() self.result_model.truncateHistory(MySettings().value("historyLength")) self.result_model.setLoading(True) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.finders_to_start = [] for finder in list(self.finders.values()): if finder.activated(): self.finders_to_start.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.finders_to_start) > 0: finder = self.finders_to_start[0] self.finders_to_start.remove(finder) self.result_model.addResult(finder.name) finder.start(to_find, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.finders_to_start = [] for finder in list(self.finders.values()): if finder.is_running(): finder.stop() self.finished(None) def result_found(self, finder, layername, value, geometry, srid): self.result_model.addResult(finder.name, layername, value, geometry, srid) self.result_view.expandAll() def limit_reached(self, finder, layername): self.result_model.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.finders_to_start) > 0: return for finder in list(self.finders.values()): if finder.is_running(): return self.running = False self.search_finished.emit() self.result_model.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.result_model.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.result_model.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) geometry = self.transform_geom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoom_to_rubberband() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) self.rubber.reset(child.geometry.type()) for i in range(0, item.rowCount()): geometry = self.transform_geom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoom_to_rubberband() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transform_geom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapSettings().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform( QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance())) return geom def zoom_to_rubberband(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class RectByFixedExtentTool(QgsMapTool): msgbar = pyqtSignal(str) rbFinished = pyqtSignal(object) rb_reset_signal = pyqtSignal() def __init__(self, canvas, x_length, y_length): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.nbPoints = 0 self.rb = None self.x_p1, self.y_p1, self.x_p2, self.y_p2, self.x_p3, self.y_p3, self.x_p4, self.y_p4 = None, None, None, None, None, None, None, None self.distance = QgsDistanceArea() self.distance.setSourceCrs(QgsCoordinateReferenceSystem(4326), QgsProject.instance().transformContext()) self.distance.setEllipsoid('WGS84') self.fixed_p2, self.fixed_p3 = QgsPointXY(0, 0), QgsPointXY(0, 0) self.length = 0 self.mCtrl = None self.x_length = x_length self.y_length = y_length self.bearing = 0.0 # our own fancy cursor self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #1210f3", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.rectangle = Rectangle() def keyPressEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = True def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = False if event.key() == Qt.Key_Escape: self.nbPoints = 0 self.x_p1, self.y_p1, self.x_p2, self.y_p2, self.x_p3, self.y_p3 = None, None, None, None, None, None self.fixed_p2, self.fixed_p3 = None, None if self.rb: self.rb.reset(True) self.rb = None self.canvas.refresh() self.rb_reset_signal.emit() return def canvasPressEvent(self, event): layer = self.canvas.currentLayer() if self.nbPoints == 0: color = QColor(255, 0, 0, 128) if self.rb: self.rb.reset() self.rb = None self.rb = QgsRubberBand(self.canvas, True) self.rb.setColor(color) self.rb.setWidth(1) self.msgbar.emit("Define bearing along track") self.rb_reset_signal.emit() elif self.nbPoints == 2: self.canvas.refresh() point = self.toLayerCoordinates(layer, event.pos()) point_map = self.toMapCoordinates(layer, point) if self.nbPoints == 0: self.x_p1 = point_map.x() self.y_p1 = point_map.y() elif self.nbPoints == 1: self.x_p2 = point_map.x() self.y_p2 = point_map.y() self.bearing = self.distance.bearing( QgsPointXY(self.x_p1, self.y_p1), QgsPointXY(self.x_p2, self.y_p2)) self.msgbar.emit("Define across track direction") else: self.x_p3 = point_map.x() self.y_p3 = point_map.y() self.nbPoints += 1 if self.nbPoints == 3: geom = self.rectangle.get_rect_by3_points( QgsPointXY(self.x_p1, self.y_p1), self.fixed_p2, self.fixed_p3, self.y_length) self.rb.setToGeometry(geom, None) self.nbPoints = 0 self.x_p1, self.y_p1, self.x_p2, self.y_p2, self.x_p3, self.y_p3 = None, None, None, None, None, None self.fixed_p2, self.fixed_p3 = None, None self.msgbar.emit("") self.rbFinished.emit(geom) if self.rb: return def canvasMoveEvent(self, event): if not self.rb: return currpoint = self.toMapCoordinates(event.pos()) if self.nbPoints == 1: self.bearing = self.distance.bearing( QgsPointXY(self.x_p1, self.y_p1), currpoint) self.fixed_p2 = self.distance.computeSpheroidProject( QgsPointXY(self.x_p1, self.y_p1), self.x_length, self.bearing) self.rb.setToGeometry( QgsGeometry.fromPolyline( [QgsPoint(self.x_p1, self.y_p1), QgsPoint(self.fixed_p2)]), None) curr_bearing = degrees(self.bearing) if curr_bearing < 0.0: curr_bearing = 360 + curr_bearing self.msgbar.emit( "Current distance: {:.3F} m, Current bearing: {:.3F} degrees". format(self.x_length, curr_bearing)) if self.nbPoints >= 2: # test if currpoint is left or right of the line defined by p1 and p2 side = calc_is_collinear(QgsPointXY(self.x_p1, self.y_p1), self.fixed_p2, currpoint) if side == 0: return None self.fixed_p3 = self.distance.computeSpheroidProject( QgsPointXY(self.x_p2, self.y_p2), self.y_length, self.bearing + radians(90) * side) geom = self.rectangle.get_rect_by3_points( QgsPointXY(self.x_p1, self.y_p1), self.fixed_p2, self.fixed_p3, self.y_length) self.rb.setToGeometry(geom, None) curr_bearing = degrees(self.bearing + radians(90) * side) if curr_bearing < 0.0: curr_bearing = 360 + curr_bearing self.msgbar.emit( "Current distance: {:.3F} m, Current bearing: {:.3F} degrees". format(self.y_length, curr_bearing)) def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): self.nbPoints = 0 self.x_p1, self.y_p1, self.x_p2, self.y_p2, self.x_p3, self.y_p3 = None, None, None, None, None, None self.fixed_p2, self.fixed_p3 = None, None if self.rb: self.rb.reset(True) self.rb.hide() self.rb = None self.canvas.refresh() def is_zoom_tool(self): return False def is_transient(self): return False def is_edit_tool(self): return True
class run(QObject): def __init__(self, id, gtotool, config, debug): super(run, self).__init__() self.debug = debug self.gtotool = gtotool self.gtomain = gtotool.gtomain self.info = gtotool.info self.iface = self.gtotool.iface self.act = self.sender() self.act.setCheckable(True) self.sourcefeat = None self.rubbers = [] try: # create rubberband self.rubber = QgsRubberBand(self.iface.mapCanvas(), QgsWkbTypes.LineGeometry) self.rubber.setColor(QColor(Qt.blue)) self.rubber.setLineStyle(Qt.PenStyle(Qt.DashDotLine)) self.rubber.setWidth(2) # get metadata self.sourcelayer = QgsProject.instance().mapLayersByName(config["sourcelayer"])[0] self.targetlayer = QgsProject.instance().mapLayersByName(config['targetlayer'])[0] self.iface.setActiveLayer(self.targetlayer) # start edit if not self.targetlayer.isEditable(): self.targetlayer.startEditing() if not self.sourcelayer.isEditable(): self.sourcelayer.startEditing() # mouse move self.canvas = self.iface.mapCanvas() self.canvas.xyCoordinates.connect(self.mouse_move) # set maptool self.mapTool = QgsMapToolIdentifyFeature(self.canvas) self.mapTool.setLayer(self.sourcelayer) self.canvas.setMapTool(self.mapTool) self.mapTool.featureIdentified.connect(self.feature_Identified) self.mapTool.deactivated.connect(self.reset_tool) self.act.setChecked(True) except Exception as e: self.info.err(e) def mouse_move(self, pointXY): try: if self.sourcefeat is not None: if self.rubber.numberOfVertices() > 1: self.rubber.removeLastPoint() self.rubber.addPoint(pointXY) except Exception as e: self.info.err(e) def feature_Identified(self, feature): try: if self.sourcefeat is None: self.sourcefeat = feature self.mapTool.setLayer(self.targetlayer) else: # transform geo = self.sourcefeat.geometry() sourceCrs = self.sourcelayer.crs() destCrs = self.targetlayer.crs() tr = QgsCoordinateTransform(sourceCrs, destCrs, QgsProject.instance()) geo.transform(tr) # change geometry self.targetlayer.beginEditCommand("New feature") self.targetlayer.changeGeometry(feature.id(), geo) self.targetlayer.endEditCommand() # cleanup self.rubber.reset() self.iface.mapCanvas().refresh() self.mapTool.setLayer(self.sourcelayer) self.sourcefeat = None print("feature selected : " + str(feature.id())) except Exception as e: self.info.err(e) def reset_tool(self): try: self.act.setChecked(False) self.iface.mapCanvas().scene().removeItem(self.rubber) except Exception as e: self.info.err(e)
class DiscoveryPlugin: def __init__(self, _iface): # Save reference to the QGIS interface self.iface = _iface # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Variables to facilitate delayed queries and database connection management self.db_timer = QTimer() self.line_edit_timer = QTimer() self.line_edit_timer.setSingleShot(True) self.line_edit_timer.timeout.connect(self.reset_line_edit_after_move) self.next_query_time = None self.last_query_time = time.time() self.db_conn = None self.search_delay = 0.5 # s self.query_sql = '' self.query_text = '' self.query_dict = {} self.db_idle_time = 60.0 # s self.display_time = 5000 # ms self.bar_info_time = 30 # s self.search_results = [] self.limit_results = 1000 self.tool_bar = None self.search_line_edit = None self.completer = None self.conn_info = {} self.marker = QgsVertexMarker(iface.mapCanvas()) self.marker.setIconSize(15) self.marker.setPenWidth(2) self.marker.setColor(QColor(226, 27, 28)) #51,160,44)) self.marker.setZValue(11) self.marker.setVisible(False) self.marker2 = QgsVertexMarker(iface.mapCanvas()) self.marker2.setIconSize(16) self.marker2.setPenWidth(4) self.marker2.setColor(QColor(255, 255, 255, 200)) self.marker2.setZValue(10) self.marker2.setVisible(False) self.is_displayed = False self.rubber_band = QgsRubberBand(iface.mapCanvas(), False) self.rubber_band.setVisible(False) self.rubber_band.setWidth(3) self.rubber_band.setStrokeColor(QColor(226, 27, 28)) self.rubber_band.setFillColor(QColor(226, 27, 28, 63)) def initGui(self): # Create a new toolbar self.tool_bar = self.iface.addToolBar('Discovery') self.tool_bar.setObjectName('Discovery_Plugin') # Create action that will start plugin configuration self.action_config = QAction( QIcon(os.path.join(self.plugin_dir, "discovery_logo.png")), u"Configure Discovery", self.tool_bar) self.action_config.triggered.connect(self.show_config_dialog) self.tool_bar.addAction(self.action_config) # Add combobox for configs self.config_combo = QComboBox() settings = QgsSettings() settings.beginGroup("/Discovery") config_list = settings.value("config_list") if config_list: for conf in config_list: self.config_combo.addItem(conf) elif settings.childGroups(): # support for prev version key = "Config1" config_list = [] config_list.append(key) settings.setValue("config_list", config_list) self.config_combo.addItem(key) settings.setValue(key + "data_type", settings.value("data_type")) settings.setValue(key + "file", settings.value("file")) settings.setValue(key + "connection", settings.value("connection")) settings.setValue(key + "schema", settings.value("schema")) settings.setValue(key + "table", settings.value("table")) settings.setValue(key + "search_column", settings.value("search_column")) settings.setValue(key + "escape_spec_chars", settings.value("escape_spec_chars")) settings.setValue(key + "echo_search_column", settings.value("echo_search_column")) settings.setValue(key + "display_columns", settings.value("display_columns")) settings.setValue(key + "geom_column", settings.value("geom_column")) settings.setValue(key + "scale_expr", settings.value("scale_expr")) settings.setValue(key + "bbox_expr", settings.value("bbox_expr")) delete_config_from_settings("", settings) self.tool_bar.addWidget(self.config_combo) # Add search edit box self.search_line_edit = QgsFilterLineEdit() self.search_line_edit.setPlaceholderText('Search for...') self.search_line_edit.setMaximumWidth(768) self.tool_bar.addWidget(self.search_line_edit) self.config_combo.currentIndexChanged.connect(self.change_configuration) # Set up the completer self.completer = QCompleter([]) # Initialise with en empty list self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setMaxVisibleItems(1000) self.completer.setModelSorting(QCompleter.UnsortedModel) # Sorting done in PostGIS self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) # Show all fetched possibilities self.completer.activated[QModelIndex].connect(self.on_result_selected) self.completer.highlighted[QModelIndex].connect(self.on_result_highlighted) self.search_line_edit.setCompleter(self.completer) # Connect any signals self.search_line_edit.textEdited.connect(self.on_search_text_changed) # Search results self.search_results = [] # Set up a timer to periodically perform db queries as required self.db_timer.timeout.connect(self.do_db_operations) self.db_timer.start(100) # Read config self.read_config(config_list[0] if config_list else "") self.locator_filter = locator_filter.DiscoveryLocatorFilter(self) self.iface.registerLocatorFilter(self.locator_filter) # Debug # import pydevd; pydevd.settrace('localhost', port=5678) def unload(self): # Stop timer self.db_timer.stop() # Disconnect any signals self.db_timer.timeout.disconnect(self.do_db_operations) self.completer.highlighted[QModelIndex].disconnect(self.on_result_highlighted) self.completer.activated[QModelIndex].disconnect(self.on_result_selected) self.search_line_edit.textEdited.disconnect(self.on_search_text_changed) # Remove the new toolbar self.tool_bar.clear() # Clear all actions self.iface.mainWindow().removeToolBar(self.tool_bar) self.iface.deregisterLocatorFilter(self.locator_filter) self.locator_filter = None def clear_suggestions(self): model = self.completer.model() model.setStringList([]) def on_search_text_changed(self, new_search_text): """ This function is called whenever the user modified the search text 1. Open a database connection 2. Make the query 3. Update the QStringListModel with these results 4. Store the other details in self.search_results """ self.query_text = new_search_text if len(new_search_text) < 3: # Clear any previous suggestions in case the user is 'backspacing' self.clear_suggestions() return if self.data_type == "postgres": query_text, query_dict = dbutils.get_search_sql( new_search_text, self.postgisgeomcolumn, self.postgissearchcolumn, self.echosearchcolumn, self.postgisdisplaycolumn, self.extra_expr_columns, self.postgisschema, self.postgistable, self.escapespecchars, self.limit_results ) self.schedule_search(query_text, query_dict) elif self.data_type == "gpkg": query_text = ( new_search_text, self.postgissearchcolumn, self.echosearchcolumn, self.postgisdisplaycolumn.split(","), self.extra_expr_columns, self.layer, self.limit_results ) self.schedule_search(query_text, None) elif self.data_type == "mssql": query_text = mssql_utils.get_search_sql( new_search_text, self.postgisgeomcolumn, self.postgissearchcolumn, self.echosearchcolumn, self.postgisdisplaycolumn, self.extra_expr_columns, self.postgisschema, self.postgistable, self.limit_results ) self.schedule_search(query_text, None) def do_db_operations(self): if self.next_query_time is not None and self.next_query_time < time.time(): # It's time to run a query self.next_query_time = None # Prevent this query from being repeated self.last_query_time = time.time() self.perform_search() else: # We're not performing a query, close the db connection if it's been open for > 60s if time.time() > self.last_query_time + self.db_idle_time: self.db_conn = None def perform_search(self): db = self.get_db() self.search_results = [] suggestions = [] if self.data_type == "postgres": cur = db.cursor() try: cur.execute(self.query_sql, self.query_dict) except psycopg2.Error as e: err_info = "Failed to execute the search query. Please, check your settings. Error message:\n\n" err_info += f"{e.pgerror}" QMessageBox.critical(None, "Discovery", err_info) return result_set = cur.fetchall() elif self.data_type == "mssql": result_set = mssql_utils.execute(db, self.query_sql) elif self.data_type == "gpkg": result_set = gpkg_utils.search_gpkg(*self.query_sql) for row in result_set: geom, epsg, suggestion_text = row[0], row[1], row[2] extra_data = {} for idx, extra_col in enumerate(self.extra_expr_columns): extra_data[extra_col] = row[3+idx] self.search_results.append((geom, epsg, suggestion_text, extra_data)) suggestions.append(suggestion_text) model = self.completer.model() model.setStringList(suggestions) self.completer.complete() def schedule_search(self, query_text, query_dict): # Update the search text and the time after which the query should be executed self.query_sql = query_text self.query_dict = query_dict self.next_query_time = time.time() + self.search_delay def show_bar_info(self, info_text): """Optional show info bar message with selected result information""" self.iface.messageBar().clearWidgets() if self.bar_info_time: self.iface.messageBar().pushMessage("Discovery", info_text, level=Qgis.Info, duration=self.bar_info_time) def on_result_selected(self, result_index): # What to do when the user makes a selection self.select_result(self.search_results[result_index.row()]) def select_result(self, result_data): geometry_text, src_epsg, suggestion_text, extra_data = result_data location_geom = QgsGeometry.fromWkt(geometry_text) canvas = self.iface.mapCanvas() dst_srid = canvas.mapSettings().destinationCrs().authid() transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem(src_epsg), QgsCoordinateReferenceSystem(dst_srid), canvas.mapSettings().transformContext()) # Ensure the geometry from the DB is reprojected to the same SRID as the map canvas location_geom.transform(transform) location_centroid = location_geom.centroid().asPoint() # show temporary marker if location_geom.type() == QgsWkbTypes.PointGeometry: self.show_marker(location_centroid) elif location_geom.type() == QgsWkbTypes.LineGeometry or \ location_geom.type() == QgsWkbTypes.PolygonGeometry: self.show_line_rubber_band(location_geom) else: #unsupported geometry type pass # Adjust map canvas extent zoom_method = 'Move and Zoom' if zoom_method == 'Move and Zoom': # with higher priority try to use exact bounding box to zoom to features (if provided) bbox_str = eval_expression(self.bbox_expr, extra_data) rect = bbox_str_to_rectangle(bbox_str) if rect is not None: # transform the rectangle in case of OTF projection rect = transform.transformBoundingBox(rect) else: # bbox is not available - so let's just use defined scale # compute target scale. If the result is 2000 this means the target scale is 1:2000 rect = location_geom.boundingBox() if rect.isEmpty(): scale_denom = eval_expression(self.scale_expr, extra_data, default=2000.) rect = canvas.mapSettings().extent() rect.scale(scale_denom / canvas.scale(), location_centroid) else: # enlarge geom bbox to have some margin rect.scale(1.2) canvas.setExtent(rect) elif zoom_method == 'Move': current_extent = QgsGeometry.fromRect(self.iface.mapCanvas().extent()) dx = location_centroid.x() - location_centroid.x() dy = location_centroid.y() - location_centroid.y() current_extent.translate(dx, dy) canvas.setExtent(current_extent.boundingBox()) canvas.refresh() self.line_edit_timer.start(0) if self.info_to_clipboard: QApplication.clipboard().setText(suggestion_text) suggestion_text += ' (copied to clipboard)' self.show_bar_info(suggestion_text) def on_result_highlighted(self, result_idx): self.line_edit_timer.start(0) def reset_line_edit_after_move(self): self.search_line_edit.setText(self.query_text) def get_db(self): # Create a new new connection if required if self.db_conn is None: if self.data_type == "postgres": self.db_conn = dbutils.get_connection(self.conn_info) elif self.data_type == "mssql": self.db_conn = mssql_utils.get_mssql_conn(self.conn_info) return self.db_conn def change_configuration(self): self.search_line_edit.setText("") self.line_edit_timer.start(0) self.read_config(self.config_combo.currentText()) def read_config(self, key=""): # the following code reads the configuration file which setups the plugin to search in the correct database, # table and method settings = QgsSettings() settings.beginGroup("/Discovery") connection = settings.value(key + "connection", "", type=str) self.data_type = settings.value(key + "data_type", "", type=str) self.file = settings.value(key + "file", "", type=str) self.postgisschema = settings.value(key + "schema", "", type=str) self.postgistable = settings.value(key + "table", "", type=str) self.postgissearchcolumn = settings.value(key + "search_column", "", type=str) self.escapespecchars = settings.value(key + "escape_spec_chars", False, type=bool) self.echosearchcolumn = settings.value(key + "echo_search_column", True, type=bool) self.postgisdisplaycolumn = settings.value(key + "display_columns", "", type=str) self.postgisgeomcolumn = settings.value(key + "geom_column", "", type=str) if settings.value("marker_time_enabled", True, type=bool): self.display_time = settings.value("marker_time", 5000, type=int) else: self.display_time = -1 if settings.value("bar_info_time_enabled", True, type=bool): self.bar_info_time = settings.value("bar_info_time", 30, type=int) else: self.bar_info_time = 0 self.limit_results = settings.value("limit_results", 1000, type=int) self.info_to_clipboard = settings.value("info_to_clipboard", True, type=bool) scale_expr = settings.value(key + "scale_expr", "", type=str) bbox_expr = settings.value(key + "bbox_expr", "", type=str) if self.is_displayed: self.hide_marker() self.hide_rubber_band() self.is_displayed = False self.make_enabled(False) # assume the config is invalid first self.db_conn = None if self.data_type == "postgres": self.conn_info = dbutils.get_postgres_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage("Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return if self.data_type == "mssql": self.conn_info = mssql_utils.get_mssql_conn_info(connection) self.layer = None if len(connection) == 0 or len(self.postgisschema) == 0 or len(self.postgistable) == 0 or \ len(self.postgissearchcolumn) == 0 or len(self.postgisgeomcolumn) == 0: return if len(self.conn_info) == 0: iface.messageBar().pushMessage("Discovery", "The database connection '%s' does not exist!" % connection, level=Qgis.Critical) return elif self.data_type == "gpkg": self.layer = QgsVectorLayer(self.file + '|layername=' + self.postgistable, self.postgistable, 'ogr') self.conn_info = None self.extra_expr_columns = [] self.scale_expr = None self.bbox_expr = None self.make_enabled(True) # optional scale expression when zooming in to results if len(scale_expr) != 0: expr = QgsExpression(scale_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid scale expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.scale_expr = scale_expr self.extra_expr_columns += expr.referencedColumns() # optional bbox expression when zooming in to results if len(bbox_expr) != 0: expr = QgsExpression(bbox_expr) if expr.hasParserError(): iface.messageBar().pushMessage("Discovery", "Invalid bbox expression: " + expr.parserErrorString(), level=Qgis.Warning) else: self.bbox_expr = bbox_expr self.extra_expr_columns += expr.referencedColumns() def show_config_dialog(self): dlg = config_dialog.ConfigDialog() if (self.config_combo.currentIndex() >= 0): dlg.configOptions.setCurrentIndex(self.config_combo.currentIndex()) if dlg.exec_(): dlg.write_config() self.config_combo.clear() for key in [dlg.configOptions.itemText(i) for i in range(dlg.configOptions.count())]: self.config_combo.addItem(key) self.config_combo.setCurrentIndex(dlg.configOptions.currentIndex()) self.change_configuration() def make_enabled(self, enabled): self.search_line_edit.setEnabled(enabled) self.search_line_edit.setPlaceholderText("Search for..." if enabled else "Search disabled: check configuration") def show_marker(self, point): for m in [self.marker, self.marker2]: m.setCenter(point) m.setOpacity(1.0) m.setVisible(True) if self.display_time == -1: self.is_displayed = True else: QTimer.singleShot(self.display_time, self.hide_marker) def hide_marker(self): opacity = self.marker.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.marker.setOpacity(opacity) self.marker2.setOpacity(opacity) QTimer.singleShot(100, self.hide_marker) else: self.marker.setVisible(False) self.marker2.setVisible(False) def show_line_rubber_band(self, geom): self.rubber_band.reset(geom.type()) self.rubber_band.setToGeometry(geom, None) self.rubber_band.setVisible(True) self.rubber_band.setOpacity(1.0) self.rubber_band.show() if self.display_time == -1: self.is_displayed = True else: QTimer.singleShot(self.display_time, self.hide_rubber_band) pass def hide_rubber_band(self): opacity = self.rubber_band.opacity() if opacity > 0.: # produce a fade out effect opacity -= 0.1 self.rubber_band.setOpacity(opacity) QTimer.singleShot(100, self.hide_rubber_band) else: self.rubber_band.setVisible(False) self.rubber_band.hide()
class LineMapTool(QgsMapToolEmitPoint): """ Class implements rubberband line tool for polygon division """ def __init__(self, iface): """ initialize rubberband line drawing :param iface: interface to QGIS """ self.iface = iface self.layer = None self.canvas = self.iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.startPoint = None self.endPoint = None self.reset() def reset(self): """ reset rubberband line tool to original state """ self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Line) def canvasPressEvent(self, e): """ handler to handle left button down, start rubberband line :param e: event """ al = self.iface.activeLayer() if al is None or al.type() != QgsMapLayer.VectorLayer or \ al.geometryType() != 2: QMessageBox.warning(self.iface.mainWindow(), tr("Warning"), tr("Actual layer contains no polygons")) return if len(al.selectedFeatures()) != 1: QMessageBox.warning( self.iface.mainWindow(), tr("Warning"), tr("Not a single polygon is selected in active layer")) return self.layer = al self.startPoint = self.toMapCoordinates(e.pos()) # snap to point on active layer line_tolerance = float(QSettings().value( "SurveyingCalculation/line_tolerance", config.line_tolerance)) self.layer.snapPoint(self.startPoint, line_tolerance) self.endPoint = self.startPoint self.isEmittingPoint = True self.showLine() def canvasReleaseEvent(self, e): """ Handler to handle left button up, end rubberband line :param e: event """ self.isEmittingPoint = False if self.startPoint.x() == self.endPoint.x() or \ self.startPoint.y() == self.endPoint.y(): return self.divide() def canvasMoveEvent(self, e): """ handler to handle mouse move event :param e: event """ if not self.isEmittingPoint: return line_tolerance = float(QSettings().value( "SurveyingCalculation/line_tolerance", config.line_tolerance)) self.endPoint = self.toMapCoordinates(e.pos()) self.layer.snapPoint(self.endPoint, line_tolerance) self.showLine() def showLine(self): """ Draw rubberband line """ self.rubberBand.reset(QGis.Line) if self.startPoint.x() == self.endPoint.x() or \ self.startPoint.y() == self.endPoint.y(): return self.rubberBand.addPoint(self.startPoint, False) self.rubberBand.addPoint(self.endPoint, True) self.rubberBand.show() def deactivate(self): """ deactivate line tool """ try: self.rubberBand.reset(QGis.Line) # erase rubberband line except: pass super(LineMapTool, self).deactivate() self.emit(SIGNAL("deactivated()")) def divide(self): """ Divide the selected polygon. :param area: area to divide (float) :param rotate: rotate/offset True/False (bool) """ selection = self.layer.selectedFeatures() if len(selection) != 1: QMessageBox.warning( self.iface.mainWindow(), tr("Warning"), tr("Not a single polygon is selected in active layer")) return feat = selection[0] # feature to divide geom = feat.geometry() if geom is None: return save_geom = QgsGeometry(geom) # save original geometry # change to layer coordinates point1 = self.toLayerCoordinates( self.layer, QgsPoint(self.startPoint.x(), self.startPoint.y())) point2 = self.toLayerCoordinates( self.layer, QgsPoint(self.endPoint.x(), self.endPoint.y())) # show coordinates to change coord_dlg = CoordDialog(point1, point2) if not coord_dlg.exec_(): return point1 = QgsPoint(float(coord_dlg.ui.StartEast.text()), \ float(coord_dlg.ui.StartNorth.text())) point2 = QgsPoint(float(coord_dlg.ui.EndEast.text()), \ float(coord_dlg.ui.EndNorth.text())) # center of rotation point0 = point1 rotate = True while geom.contains(point1): # extend line outside polygon point1 = QgsPoint(point1.x() - (point2.x() - point1.x()) * 10, \ point1.y() - (point2.y() - point1.y()) * 10) while geom.contains(point2): # extend line outside polygon point2 = QgsPoint(point2.x() + (point2.x() - point1.x()) * 10, \ point2.y() + (point2.y() - point1.y()) * 10) geom_line = QgsGeometry.fromPolyline([point1, point2]) # divider if not geom.intersects(geom_line): if QMessageBox.question(self.iface.mainWindow(), \ tr("Question"), tr("Line does not intersects polygon, line will be shifted into the polygon. Do you want to continue?"), \ QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return # find an internal point if QGis.QGIS_VERSION > '2.4': cp = geom.pointOnSurface().vertexAt(0) else: cp = geom.centroid().vertexAt(0) # offset line to go through cp dx = point2.x() - point1.x() dy = point2.y() - point1.y() point0 = QgsPoint(cp.x(), cp.y()) point1 = QgsPoint(cp.x() - 1000.0 * dx, cp.y() - 1000.0 * dy) point2 = QgsPoint(cp.x() + 1000.0 * dx, cp.y() + 1000.0 * dy) rotate = False # divide polygon result, new_geoms, test_points = geom.splitGeometry([point1, point2], True) if result != 0: QMessageBox.warning(self.iface.mainWindow(), tr("Warning"), tr("Area division failed ") + str(result)) return # open modal dialog area_dlg = AreaDialog(save_geom.area(), geom.area(), rotate) if not area_dlg.exec_(): return area = float(area_dlg.ui.AreaLineEdit.text()) rotate = area_dlg.ui.OnePointRadio.isChecked() if save_geom.area() <= area: QMessageBox.warning( self.iface.mainWindow(), tr("Warning"), tr("Area of polygon is smaller then requested area")) return area_tolerance = float(QSettings().value( "SurveyingCalculation/area_tolerance", config.area_tolerance)) max_iteration = int(QSettings().value( "SurveyingCalculation/max_iteration", config.max_iteration)) i = 0 #l = ((point2.x() - point1.x())**2 + (point2.y() - point1.y())**2)**0.5 while True: da = geom.area() - area if fabs(da) <= area_tolerance: break # area OK exit loop # length of intersection geom_line = QgsGeometry.fromPolyline([point1, point2]) section = save_geom.intersection(geom_line) l = section.length() # section length dir = atan2(point2.x() - point0.x(), point2.y() - point0.y()) if rotate: # change line direction b = da * 2.0 / l dir += atan(b / l) point1 = QgsPoint(point0.x() + sin(dir + pi) * 1000.0, \ point0.y() + cos(dir + pi) * 1000.0) point2 = QgsPoint(point0.x() + sin(dir) * 1000.0, \ point0.y() + cos(dir) * 1000.0) else: # offset line # perpendicular direction to line b = da / l # approximate offset dir += pi / 2.0 # perpendicular dir point1 = QgsPoint(point1.x() + sin(dir) * b, \ point1.y() + cos(dir) * b) point2 = QgsPoint(point2.x() + sin(dir) * b, \ point2.y() + cos(dir) * b) i += 1 if i > max_iteration: QMessageBox.warning( self.iface.mainWindow(), tr("Warning"), tr("Area division not finished after max iteration") + str(result)) return geom = QgsGeometry(save_geom) # continue from original geomerty while geom.contains(point1): # extend line outside polygon point1 = QgsPoint(point1.x() - (point2.x() - point1.x()) * 10, \ point1.y() - (point2.y() - point1.y()) * 10) while geom.contains(point2): # extend line outside polygon point2 = QgsPoint(point2.x() + (point2.x() - point1.x()) * 10, \ point2.y() + (point2.y() - point1.y()) * 10) result, new_geoms, test_points = geom.splitGeometry( [point1, point2], True) # refresh old geometry fid = feat.id() self.layer.dataProvider().changeGeometryValues({fid: geom}) # add new feature feat_new = QgsFeature() fields = self.layer.dataProvider().fields() feat_new.setFields(fields, True) # combine new_geoms to single geometry new_geom = QgsGeometry(new_geoms[0]) for new_g in new_geoms[1:]: new_geom = QgsGeometry(new_geom.combine(new_g)) feat_new.setGeometry(new_geom) self.layer.dataProvider().addFeatures([feat_new]) # refresh canvas self.canvas.refresh()
class InterpolateTool(QgsMapToolAdvancedDigitizing): """ Map tool class to interpolate an elevation in the middle of a segment """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/interpolate_icon.png' self.text = QCoreApplication.translate( "VDLTools", "Interpolate the elevation of a vertex and a point in the middle of a line" ) self.__layer = None self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__lastFeatureId = None self.__layerList = None self.__lastLayer = None self.__confDlg = None self.__mapPoint = None self.__rubber = None self.__selectedFeature = None self.__findVertex = False def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) self.__updateList() self.__rubber = QgsRubberBand(self.canvas(), QGis.Point) color = QColor("red") color.setAlphaF(0.78) self.__rubber.setColor(color) self.__rubber.setIcon(4) self.__rubber.setWidth(2) self.__rubber.setIconSize(20) self.canvas().layersChanged.connect(self.__updateList) self.canvas().scaleChanged.connect(self.__updateList) self.setMode(self.CaptureLine) def deactivate(self): """ When the action is deselected """ self.__done() self.__cancel() self.__rubber = None Signal.safelyDisconnect(self.canvas().layersChanged, self.__updateList) Signal.safelyDisconnect(self.canvas().scaleChanged, self.__updateList) QgsMapToolAdvancedDigitizing.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def __done(self): """ When the edition is finished """ self.__isEditing = False self.__confDlg = None self.__mapPoint = None def __cancel(self): """ To cancel used variables """ self.__findVertex = False if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = None if self.__rubber is not None: self.__rubber.reset() self.__lastFeatureId = None self.__selectedFeature = None def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type( ) == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.action().setEnabled(False) self.__removeLayer() def __updateList(self): """ To update the line layers list that we can use for interpolation """ self.__layerList = [] for layer in self.canvas().layers(): if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType() \ and layer.geometryType() == QGis.Line: self.__layerList.append( QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.All, 10, QgsTolerance.Pixels)) def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__done() self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and self.__layerList is not None: f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), self.__layerList) if f_l is not None and self.__lastFeatureId != f_l[0].id(): f = f_l[0] self.__lastFeatureId = f.id() if self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastLayer = f_l[1] self.__lastLayer.setSelectedFeatures([f.id()]) if f_l is None and self.__lastLayer is not None: self.__lastLayer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(map_point, self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(4) self.__rubber.setToGeometry( QgsGeometry().fromPoint(point), None) else: intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: self.__rubber.setIcon(1) self.__rubber.setToGeometry( QgsGeometry().fromPoint(intersection), None) if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: self.__rubber.setIcon(1) self.__rubber.setToGeometry( QgsGeometry().fromPoint(intersection), None) elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): self.__rubber.setIcon(3) self.__rubber.setToGeometry( QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if self.__lastLayer is not None and not self.__findVertex: found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] self.__iface.messageBar().pushMessage( QCoreApplication.translate( "VDLTools", "Select the position for interpolation (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.setMode(self.CaptureNone) self.__findVertex = True elif self.__findVertex: self.__rubber.reset() snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All) match = Finder.snap(event.mapPoint(), self.canvas(), snap_layers) if match.hasVertex() or match.hasEdge(): point = match.point() ok = False noVertex = False if match.hasVertex(): if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True noVertex = True else: intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self, self.__selectedFeature.id()) if intersection is not None: point = intersection ok = True elif self.__selectedFeature.id() == match.featureId() \ and match.layer().id() == self.__lastLayer.id(): ok = True if ok: self.__isEditing = True self.__findVertex = False self.__mapPoint = point if noVertex: self.__ok(False, True) else: self.__confDlg = InterpolateConfirmDialog() if self.__lastLayer.isEditable(): self.__confDlg.setMainLabel( QCoreApplication.translate( "VDLTools", "What do you want to do ?")) self.__confDlg.setAllLabel( QCoreApplication.translate( "VDLTools", "Create point and new vertex")) self.__confDlg.setVtLabel( QCoreApplication.translate( "VDLTools", "Create only the vertex")) self.__confDlg.rejected.connect(self.__done) self.__confDlg.okButton().clicked.connect( self.__onConfirmOk) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmCancel) self.__confDlg.show() else: self.__done() self.__cancel() def __onConfirmCancel(self): """ When the Cancel button in Interpolate Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmOk(self): """ When the Ok button in Interpolate Confirm Dialog is pushed """ checkedId = self.__confDlg.getCheckedId() self.__confDlg.accept() withVertex = True withPoint = True if checkedId == 1: withVertex = False else: if not self.__lastLayer.isEditable(): self.__lastLayer.startEditing() if checkedId == 2: withPoint = False self.__ok(withVertex, withPoint) def __ok(self, withVertex, withPoint): """ To realize the interpolation :param withVertex: if we want a new interpolated vertex :param withPoint: if we want a new interpolated point """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry(), self.__iface) vertex_v2 = QgsPointV2() vertex_id = QgsVertexId() line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0) x0 = line_v2.xAt(vertex_id.vertex - 1) y0 = line_v2.yAt(vertex_id.vertex - 1) d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y()) x1 = line_v2.xAt(vertex_id.vertex) y1 = line_v2.yAt(vertex_id.vertex) d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y()) z0 = line_v2.zAt(vertex_id.vertex - 1) z1 = line_v2.zAt(vertex_id.vertex) vertex_v2.addZValue(old_div((d0 * z1 + d1 * z0), (d0 + d1))) if withPoint: pt_feat = QgsFeature(self.__layer.pendingFields()) pt_feat.setGeometry(QgsGeometry(vertex_v2)) for i in range(len(self.__layer.pendingFields())): # default = self.__layer.defaultValue(i, pt_feat) # if default is not None: # print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default) # print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i)) e = QgsExpression(self.__layer.defaultValueExpression(i)) default = e.evaluate(pt_feat) pt_feat.setAttribute(i, default) if self.__layer.editFormConfig().suppress( ) == QgsEditFormConfig.SuppressOn: self.__layer.addFeature(pt_feat) else: self.__iface.openFeatureForm(self.__layer, pt_feat) if withVertex: line_v2.insertVertex(vertex_id, vertex_v2) self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2)) found_features = self.__lastLayer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) else: self.__selectedFeature = found_features[0] else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No more feature selected"), level=QgsMessageBar.INFO) self.__iface.mapCanvas().refresh() self.__done() self.__findVertex = True
class AttributePainterClass: def __init__(self, iface): # Save reference to the QGIS interface self.iface = iface self.canvas = self.iface.mapCanvas() # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # Source feature rubberband definition colorSource = QColor(250, 0, 0, 200) self.sourceEvid = QgsRubberBand(self.canvas) self.sourceEvid.setColor(colorSource) self.sourceEvid.setWidth(3) self.dock = attributePainterDialog(self.iface) #connect signals self.dock.layerCombo.currentIndexChanged.connect( self.layerComboChanged) self.dock.attributeCombo.currentIndexChanged.connect( self.attributeComboChanged) self.dock.labelCombo.currentIndexChanged.connect( self.labelComboChanged) self.dock.paintingButton.clicked.connect(self.startPainting) self.dock.saveButton.clicked.connect(self.startPainting) #plugin state self.pluginIsActive = False #painting state self.painting = False def initGui(self): # Create action that will show plugin widget self.action = QAction( QIcon(os.path.join(self.plugin_dir, "icons/select2.png")), # the following does not seem to work anymore, maybe since PyQt5?! #QIcon(':/plugins/LayerFeatureLabeler/icons/select.png'), u"LayerFeatureLabeler", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&VectorLayerTool", self.action) #creating dock view intance # self.dock = attributePainterDialog(self.iface) self.dockwidget = QDockWidget("Layer Feature Labeler", self.iface.mainWindow()) self.dockwidget.setObjectName("Layer Feature Labeler") self.dockwidget.setWidget(self.dock) self.layerHighlighted = None self.sourceFeat = None # here the map tool is choosen self.sourceMapTool = IdentifyGeometry(self.canvas, pickMode='active') self.destinationMapTool = IdentifyGeometry(self.canvas, pickMode='active') def run(self): # show the dockwidget # self.loadProjectFile() self.initLayerCombo() self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget) self.dockwidget.setWindowTitle('Layer Feature Labeler') self.dockwidget.show() self.pluginIsActive = True #QgsMapLayerRegistry.instance().legendLayersAdded.connect(self.initLayerCombo) #QGIS2: if version == 2: QgsMapLayerRegistry.instance().layersRemoved.connect( self.initLayerCombo) QgsMapLayerRegistry.instance().layersAdded.connect( self.initLayerCombo) elif version == 3: QgsProject.instance().layersRemoved.connect(self.initLayerCombo) QgsProject.instance().layersAdded.connect(self.initLayerCombo) else: print('only QGIS 2 and QGIS 3 is supported') self.dock.closingPlugin.connect(self.onClosePlugin) def onClosePlugin(self): print("closing plugin") # deactivate painting if self.painting == True: self.iface.mainWindow().findChild( QAction, 'mActionToggleEditing').trigger() self.painting == False self.dock.closingPlugin.disconnect(self.onClosePlugin) def loadProjectFile(self): # open the QGIS project file scenario_open = False scenario_file = os.path.join(os.path.dirname(__file__), 'data', 'project.qgs') # check if file exists if os.path.isfile(scenario_file): self.iface.addProject(scenario_file) scenario_open = True else: last_dir = uf.getLastDir("PlanningToolClass") new_file = QtWidgets.QFileDialog.getOpenFileName( self, "", last_dir, "(*.qgs)") if new_file: self.iface.addProject(unicode(new_file)) scenario_open = True def layerComboChanged(self): if self.painting == True: self.startPainting( ) # which in fact means quit painting, when the layer is changed text = str(self.dock.layerCombo.currentText()) try: #this fails if plugin is opened without a layer if version == 2: self.activeLayer = QgsMapLayerRegistry.instance( ).mapLayersByName(text)[ 0] # 0 element because it returns a list elif version == 3: self.activeLayer = QgsProject.instance().mapLayersByName( text)[0] except: return self.iface.setActiveLayer(self.activeLayer) # set the columns (=attributes) of the current layer to the attribute combo box self.initAttributeCombo(self.activeLayer) def attributeComboChanged(self): activeAttribute = str(self.dock.attributeCombo.currentText()) self.activeAttributeID = uf.getFieldIndex(self.activeLayer, activeAttribute) self.applyStyle() def labelComboChanged(self): self.activeLabel = str(self.dock.labelCombo.currentText()) def initLayerCombo(self): try: self.dock.layerCombo.currentIndexChanged.disconnect( self.layerComboChanged) except: pass layerList = [] #QGIS2 if version == 2: layerList = [ layer.name() for layer in QgsMapLayerRegistry.instance().mapLayers().values() ] elif version == 3: layerList = [ layer.name() for layer in list(QgsProject.instance().mapLayers().values()) ] else: print('unkown QGIS version') self.dock.layerCombo.clear() layerList.insert(0, '') self.dock.layerCombo.addItems(layerList) #set the currently selected layer (for the first time) self.layerComboChanged() self.dock.layerCombo.currentIndexChanged.connect( self.layerComboChanged) def initAttributeCombo(self, layer): stringFieldNames = [] stringFieldIDs = [] fields = layer.fields() for field in fields: if field.typeName() == 'String': stringFieldNames.append(field.name()) stringFieldIDs.append(fields.indexFromName(field.name())) # init label combo # do this first so the self.valColTuple is initialised self.initLabelCombo(layer, stringFieldIDs) #fieldList = uf.getFieldNames(layer) self.dock.attributeCombo.clear() stringFieldNames.insert(0, '') self.dock.attributeCombo.addItems(stringFieldNames) #set the currently selected attribute (for the first time) self.attributeComboChanged() def initLabelCombo(self, layer, ids): values = [] for item in ids: uvals = layer.uniqueValues(item) # I don't want a list of lists but a flat list, that's why I'm doing this for val in uvals: if type(val) is unicode: if uf.isNumeric(val) == False: if any(char.isdigit() for char in val) == False: values.append(val) values = sorted(set(values)) ## initialize color list self.initColors(values) self.dock.labelCombo.clear() # self.dock.labelCombo.addItems(values) for i, item in enumerate(self.valColTuple): pixmap = QPixmap(16, 16) pixmap.fill(item[1]) # self.dock.labelCombo.addItem(item[0]) self.dock.labelCombo.insertItem(i, QIcon(pixmap), item[0]) #set the currently selected label (for the first time) self.labelComboChanged() #### EDITING #### def startPainting(self): # deactivate painting if self.painting == True: self.painting = False self.activeLayer.selectionChanged.disconnect(self.applyLabel) self.dock.paintingButton.setChecked(False) self.iface.actionPan().trigger() layer = self.canvas.currentLayer() layer.removeSelection() #activate painting else: self.painting = True self.activeLayer.selectionChanged.connect(self.applyLabel) self.dock.paintingButton.setChecked(True) self.iface.actionSelect().trigger() self.iface.mainWindow().findChild(QAction, 'mActionToggleEditing').trigger() #unfortunately this does not work: # TODO: actually I can use layer.startEditing in the if, and layer.commitChanges in the else statement # self.iface.mainWindow().findChild(QAction, 'mActionToggleEditing').activate(True) # self.iface.actionSelect().activate(True) def applyLabel(self): features = self.activeLayer.selectedFeatures() for feature in features: self.activeLayer.changeAttributeValue(feature.id(), self.activeAttributeID, self.activeLabel) # self.apply_style(feature) # self.apply_style(feature) #### STYLING #### def initColors(self, values): self.valColTuple = [] for i, val in enumerate(values): r = random.randrange(0, 256, 1) g = random.randrange(0, 256, 1) b = random.randrange(0, 256, 1) self.valColTuple.append((val, QColor(r, g, b))) def applyStyle(self): #get currently active layer layer = self.activeLayer #for every possible value (=all the non-numeric values in the layer), set a color from the valColTuple categories = [] for i, attribute in enumerate(self.valColTuple): if version == 2: symbol = QgsSymbolV2.defaultSymbol(layer.geometryType()) symbol.setColor(attribute[1]) category = QgsRendererCategoryV2(attribute[0], symbol, attribute[0]) elif version == 3: symbol = QgsSymbol.defaultSymbol(layer.geometryType()) # symbol = QgsSymbol.defaultSymbol(feature.geometry().type()) symbol.setColor(attribute[1]) category = QgsRendererCategory(attribute[0], symbol, attribute[0]) else: print('only QGIS 2 and QGIS 3 is supported') categories.append(category) # Apply the colors to the currently selected column fieldName = self.dock.attributeCombo.currentText() if version == 2: renderer = QgsCategorizedSymbolRendererV2(fieldName, categories) layer.setRendererV2(renderer) elif version == 3: renderer = QgsCategorizedSymbolRenderer(fieldName, categories) layer.setRenderer(renderer) else: print('only QGIS 2 and QGIS 3 is supported') # Refresh the layer layer.triggerRepaint() def unload(self): ''' Remove the plugin widget and clear source feature highlight ''' if self.sourceFeat: self.sourceEvid.reset() self.iface.removeToolBarIcon(self.action) self.iface.removeDockWidget(self.dockwidget) # self.canvas.mapToolSet.disconnect(self.toggleMapTool) # try: # self.iface.legendInterface().currentLayerChanged.disconnect(self.checkOnLayerChange) # except: # self.iface.currentLayerChanged.disconnect(self.checkOnLayerChange) # self.iface.projectRead.disconnect(self.resetSource) if self.pluginIsActive: self.onClosePlugin()
class VoGISProfilToolMainDialog(QDialog): def __init__(self, interface, settings): self.settings = settings self.iface = interface self.selectingVisibleRasters = False QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolMain() self.ui.setupUi(self) if self.settings.onlyHektoMode is True: self.ui.IDC_widRaster.hide() self.adjustSize() self.ui.IDC_tbFromX.setText('-30000') self.ui.IDC_tbFromY.setText('240000') self.ui.IDC_tbToX.setText('-20000') self.ui.IDC_tbToY.setText('230000') self.__addRastersToGui() for lLyr in self.settings.mapData.lines.lines(): self.ui.IDC_cbLineLayers.addItem(lLyr.name, lLyr) if self.settings.mapData.lines.count() < 1: self.ui.IDC_rbDigi.setChecked(True) self.ui.IDC_rbShapeLine.setEnabled(False) #Einstellungen fuer Linie zeichen self.action = QAction( QIcon(":/plugins/vogisprofiltoolmain/icons/icon.png"), "VoGIS-Profiltool", self.iface.mainWindow()) self.action.setWhatsThis("VoGIS-Profiltool") self.canvas = self.iface.mapCanvas() self.tool = ProfiletoolMapTool(self.canvas, self.action) self.savedTool = self.canvas.mapTool() self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) if QGis.QGIS_VERSION_INT >= 10900: #self.rubberband.setBrushStyle() self.rubberband.setLineStyle(Qt.SolidLine) self.rubberband.setWidth(4.0) self.rubberband.setColor(QColor(0, 255, 0)) #http://www.qgis.org/api/classQgsRubberBand.html#a6f7cdabfcf69b65dfc6c164ce2d01fab self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def accept(self): try: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "ACCEPTED") if self.settings.onlyHektoMode is True and self.settings.mapData.rasters.count( ) > 0: self.settings.onlyHektoMode = False if self.settings.onlyHektoMode is False: if self.settings.mapData.rasters.count() < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", u"Keine Raster vorhanden. Zum Hektometrieren Dialog neu öffnen.") #return retVal = QMessageBox.warning( self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate( 'code', 'Keine Rasterebene vorhanden oder sichtbar! Nur hektometrieren?', None, QApplication.UnicodeUTF8), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if retVal == QMessageBox.No: return else: self.settings.onlyHektoMode = True self.settings.createHekto = True if self.__getSettingsFromGui() is False: return if self.settings.onlyHektoMode is False: if len(self.settings.mapData.rasters.selectedRasters()) < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "Kein Raster selektiert!") #msg = #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", msg) QMessageBox.warning( self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Kein Raster selektiert!', None, QApplication.UnicodeUTF8)) return QgsMessageLog.logMessage( 'modeLine!=line: {0}'.format( self.settings.modeLine != enumModeLine.line), 'VoGis') QgsMessageLog.logMessage( 'customLine is None: {0}'.format( self.settings.mapData.customLine is None), 'VoGis') if self.settings.modeLine != enumModeLine.line and self.settings.mapData.customLine is None: QMessageBox.warning( self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Profillinie vorhanden!', None, QApplication.UnicodeUTF8)) return #self.rubberband.reset(self.polygon) #QDialog.accept(self) QApplication.setOverrideCursor(Qt.WaitCursor) createProf = CreateProfile(self.iface, self.settings) profiles = createProf.create() QgsMessageLog.logMessage('ProfCnt: ' + str(len(profiles)), 'VoGis') if len(profiles) < 1: QApplication.restoreOverrideCursor() QMessageBox.warning( self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate( 'code', 'Es konnten keine Profile erstellt werden.', None, QApplication.UnicodeUTF8)) return dlg = VoGISProfilToolPlotDialog(self.iface, self.settings, profiles) dlg.show() #result = self.dlg.exec_() dlg.exec_() except: QApplication.restoreOverrideCursor() ex = u'{0}'.format(traceback.format_exc()) msg = 'Unexpected ERROR:\n\n{0}'.format(ex[:2000]) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", msg) def reject(self): #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "REJECTED") self.rubberband.reset(self.polygon) QDialog.reject(self) def selectVisibleRasters(self): self.refreshRasterList() self.selectingVisibleRasters = True extCanvas = self.iface.mapCanvas().extent() #alle raster in den einstellunge deselektieren for r in self.settings.mapData.rasters.rasters(): r.selected = False #alle raster in der ListView deselektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) item.setCheckState(Qt.Unchecked) #Raster im Extent selektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) if QGis.QGIS_VERSION_INT < 10900: raster = item.data(Qt.UserRole).toPyObject() else: raster = item.data(Qt.UserRole) for r in self.settings.mapData.rasters.rasters(): if extCanvas.intersects(r.grid.extent()): if r.id == raster.id: r.selected = True item.setCheckState(Qt.Checked) self.selectingVisibleRasters = False def lineLayerChanged(self, idx): if self.ui.IDC_rbShapeLine.isChecked() is False: self.ui.IDC_rbShapeLine.setChecked(True) if QGis.QGIS_VERSION_INT < 10900: lineLyr = (self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex()).toPyObject()) else: lineLyr = (self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex())) lyr = lineLyr.line #QgsMessageLog.logMessage('{0}'.format(lyr.selectedFeatureCount()), 'VoGis') #QgsMessageLog.logMessage('{0}'.format(dir(lyr)), 'VoGis') if hasattr(lyr, 'selectedFeatureCount'): if (lyr.selectedFeatureCount() < 1): self.ui.IDC_chkOnlySelectedFeatures.setChecked(False) else: self.ui.IDC_chkOnlySelectedFeatures.setChecked(True) def valueChangedEquiDistance(self, val): if self.ui.IDC_rbEquiDistance.isChecked() is False: self.ui.IDC_rbEquiDistance.setChecked(True) def valueChangedVertexCount(self, val): if self.ui.IDC_rbVertexCount.isChecked() is False: self.ui.IDC_rbVertexCount.setChecked(True) def lvRasterItemChanged(self, item): if self.selectingVisibleRasters is True: return if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False iData = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: rl = iData.toPyObject() else: rl = iData self.settings.mapData.rasters.getById(rl.id).selected = selected def refreshRasterList(self): legend = self.iface.legendInterface() availLayers = legend.layers() rColl = RasterCollection() for lyr in availLayers: if legend.isLayerVisible(lyr): lyrType = lyr.type() lyrName = unicodedata.normalize('NFKD', unicode(lyr.name())).encode( 'ascii', 'ignore') if lyrType == 1: r = Raster(lyr.id(), lyrName, lyr) rColl.addRaster(r) self.settings.mapData.rasters = rColl self.__addRastersToGui() def __addRastersToGui(self): self.ui.IDC_listRasters.clear() check = Qt.Unchecked if self.settings.mapData.rasters.count() == 1: check = Qt.Checked self.settings.mapData.rasters.rasters()[0].selected = True for rLyr in self.settings.mapData.rasters.rasters(): item = QListWidgetItem(rLyr.name) item.setData(Qt.UserRole, rLyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listRasters.addItem(item) def drawLine(self): if self.ui.IDC_rbDigi.isChecked() is False: self.ui.IDC_rbDigi.setChecked(True) self.dblckltemp = None self.rubberband.reset(self.polygon) self.__cleanDigi() self.__activateDigiTool() self.canvas.setMapTool(self.tool) def __createDigiFeature(self, pnts): u = Util(self.iface) f = u.createQgLineFeature(pnts) self.settings.mapData.customLine = f def __lineFinished(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) self.pointsToDraw.append(newPoint) #launch analyses self.iface.mainWindow().statusBar().showMessage(str(self.pointsToDraw)) if len(self.pointsToDraw) < 2: self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint self.drawnLine = None QMessageBox.warning( self, "VoGIS-Profiltool", QApplication.translate( 'code', 'Profillinie digitalisieren abgebrochen!', None, QApplication.UnicodeUTF8)) self.drawnLine = self.__createDigiFeature(self.pointsToDraw) self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint def __cleanDigi(self): self.pointsToDraw = [] self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.savedTool) def __activateDigiTool(self): QObject.connect(self.tool, SIGNAL("moved"), self.__moved) QObject.connect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.connect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.connect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) QObject.connect(self.tool, SIGNAL("deactivate"), self.__deactivateDigiTool) def __deactivateDigiTool(self): QObject.disconnect(self.tool, SIGNAL("moved"), self.__moved) QObject.disconnect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.disconnect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.disconnect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) if QGis.QGIS_VERSION_INT < 10900: self.iface.mainWindow().statusBar().showMessage(QString("")) else: self.iface.mainWindow().statusBar().showMessage('') def __moved(self, position): if len(self.pointsToDraw) > 0: mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) self.rubberband.reset(self.polygon) newPnt = QgsPoint(mapPos.x(), mapPos.y()) if QGis.QGIS_VERSION_INT < 10900: for i in range(0, len(self.pointsToDraw)): self.rubberband.addPoint(self.pointsToDraw[i]) self.rubberband.addPoint(newPnt) else: pnts = self.pointsToDraw + [newPnt] self.rubberband.setToGeometry(QgsGeometry.fromPolyline(pnts), None) def __rightClicked(self, position): self.__lineFinished(position) def __leftClicked(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) #if self.selectionmethod == 0: if newPoint == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointsToDraw) == 0: self.rubberband.reset(self.polygon) self.pointsToDraw.append(newPoint) def __doubleClicked(self, position): pass #not in use right now def __lineCancel(self): pass def __getSettingsFromGui(self): self.settings.linesExplode = ( self.ui.IDC_chkLinesExplode.checkState() == Qt.Checked) self.settings.linesMerge = ( self.ui.IDC_chkLinesMerge.checkState() == Qt.Checked) self.settings.onlySelectedFeatures = ( self.ui.IDC_chkOnlySelectedFeatures.checkState() == Qt.Checked) self.settings.equiDistance = self.ui.IDC_dblspinDistance.value() self.settings.vertexCnt = self.ui.IDC_dblspinVertexCnt.value() #self.settings.createHekto = (self.ui.IDC_chkCreateHekto.checkState() == Qt.Checked) self.settings.nodesAndVertices = ( self.ui.IDC_chkNodesAndVertices.checkState() == Qt.Checked) if QGis.QGIS_VERSION_INT < 10900: self.settings.mapData.selectedLineLyr = ( self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex()).toPyObject()) else: self.settings.mapData.selectedLineLyr = ( self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex())) if self.settings.onlySelectedFeatures is True and self.settings.mapData.selectedLineLyr.line.selectedFeatureCount( ) < 1: QMessageBox.warning( self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate( 'code', u'Der gewählte Layer hat keine selektierten Elemente.', None, QApplication.UnicodeUTF8)) return False if self.ui.IDC_rbDigi.isChecked(): self.settings.modeLine = enumModeLine.customLine elif self.ui.IDC_rbShapeLine.isChecked(): self.settings.modeLine = enumModeLine.line else: #self.ui.IDC_rbStraigthLine self.settings.modeLine = enumModeLine.straightLine if self.ui.IDC_rbEquiDistance.isChecked(): self.settings.modeVertices = enumModeVertices.equiDistant else: self.settings.modeVertices = enumModeVertices.vertexCnt if self.ui.IDC_rbStraigthLine.isChecked(): ut = Util(self.iface) if ut.isFloat( self.ui.IDC_tbFromX.text(), QApplication.translate('code', 'Rechtswert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromX = float(self.ui.IDC_tbFromX.text()) if ut.isFloat( self.ui.IDC_tbFromY.text(), QApplication.translate('code', 'Hochwert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromY = float(self.ui.IDC_tbFromY.text()) if ut.isFloat( self.ui.IDC_tbToX.text(), QApplication.translate('code', 'Rechtswert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toX = float(self.ui.IDC_tbToX.text()) if ut.isFloat( self.ui.IDC_tbToY.text(), QApplication.translate('code', 'Hochwert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toY = float(self.ui.IDC_tbToY.text()) fromPnt = QgsPoint(fromX, fromY) toPnt = QgsPoint(toX, toY) self.settings.mapData.customLine = ut.createQgLineFeature( [fromPnt, toPnt]) return True
class RectFromCenterFixedTool(QgsMapTool): msgbar = pyqtSignal(str) rbFinished = pyqtSignal(object) rb_reset_signal = pyqtSignal() def __init__(self, canvas, x_length=0.0, y_length=0.0): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.nbPoints = 0 self.rb = None self.mCtrl = None self.xc, self.yc, self.p2 = None, None, None self.distance = QgsDistanceArea() self.distance.setSourceCrs(QgsCoordinateReferenceSystem(4326), QgsProject.instance().transformContext()) self.distance.setEllipsoid('WGS84') self.x_length = x_length self.y_length = y_length self.diagonal = sqrt(self.x_length / 2 * self.x_length / 2 + self.y_length / 2 * self.y_length / 2) # our own fancy cursor self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #17a51a", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. . .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. . .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.curr_geom = None self.last_currpoint = None self.curr_angle = 0.0 self.total_angle = 0.0 self.rectangle = Rectangle() self.angle_ini_rot = 0.0 self.ini_geom = None def keyPressEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = True self.point_ini_rot = self.toMapCoordinates( self.canvas.mouseLastXY()) self.angle_ini_rot = self.curr_angle def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = False if event.key() == Qt.Key_Escape: self.nbPoints = 0 self.xc, self.yc, self.p2 = None, None, None if self.rb: self.rb.reset(True) self.rb = None self.canvas.refresh() self.rb_reset_signal.emit() return def changegeomSRID(self, geom): layer = self.canvas.currentLayer() layerCRSSrsid = layer.crs().srsid() projectCRSSrsid = QgsMapSettings().destinationCrs().srsid() if layerCRSSrsid != projectCRSSrsid: g = QgsGeometry.fromPoint(geom) g.transform(QgsCoordinateTransform(projectCRSSrsid, layerCRSSrsid)) retPoint = g.asPoint() else: retPoint = geom return retPoint def canvasPressEvent(self, event): layer = self.canvas.currentLayer() if self.nbPoints == 0: color = QColor(255, 0, 0, 128) if self.rb: self.rb.reset() self.rb = None self.rb = QgsRubberBand(self.canvas, True) self.rb.setColor(color) self.rb.setWidth(1) self.canvas.refresh() self.rb_reset_signal.emit() point = self.toLayerCoordinates(layer, event.pos()) pointMap = self.toMapCoordinates(layer, point) if self.nbPoints == 0: self.xc = pointMap.x() self.yc = pointMap.y() if self.x_length != 0: self.diagonal = sqrt(self.x_length / 2 * self.x_length / 2 + self.y_length / 2 * self.y_length / 2) self.p2 = self.distance.computeSpheroidProject( QgsPointXY(self.xc, self.yc), self.diagonal, atan2(self.y_length / 2, self.x_length / 2)) else: self.x_bearing = pointMap.x() self.y_bearing = pointMap.y() self.bearing = self.distance.bearing(QgsPointXY(self.xc, self.yc), pointMap) self.nbPoints += 1 if self.nbPoints == 2: self.nbPoints = 0 self.xc, self.yc, self.p2 = None, None, None self.last_currpoint = None self.rbFinished.emit(self.curr_geom) self.curr_geom = None self.curr_angle = 0.0 self.total_angle = 0.0 if self.rb: return def canvasMoveEvent(self, event): if not self.rb or not self.xc or not self.yc: return currpoint = self.toMapCoordinates(event.pos()) self.msgbar.emit( "Hold Ctrl to adjust track orientation. Click when finished.") if not self.mCtrl: if self.last_currpoint is None: self.last_currpoint = self.p2 self.curr_geom = self.rectangle.get_rect_from_center( QgsPointXY(self.xc, self.yc), self.last_currpoint, ) self.ini_geom = self.curr_geom self.curr_geom = self.rectangle.get_rect_projection( self.curr_geom, QgsPointXY(self.xc, self.yc), self.x_length, self.y_length) elif self.ini_geom is not None: if self.last_currpoint is None: self.last_currpoint = currpoint self.curr_geom, self.curr_angle = self.rectangle.get_rect_rotated( self.ini_geom, QgsPointXY(self.xc, self.yc), currpoint, self.point_ini_rot, self.angle_ini_rot) self.last_currpoint = currpoint self.curr_geom = self.rectangle.get_rect_projection( self.curr_geom, QgsPointXY(self.xc, self.yc), self.x_length, self.y_length) if self.curr_geom is not None: self.rb.setToGeometry(self.curr_geom, None) def show_settings_warning(self): pass def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): self.nbPoints = 0 self.xc, self.yc, self.x_p2, self.y_p2 = None, None, None, None if self.rb: self.rb.reset(True) self.rb = None self.canvas.refresh() def is_zoom_tool(self): return False def is_transient(self): return False def is_edit_tool(self): return True
class MetaSearchDialog(QDialog, BASE_CLASS): """main dialogue""" def __init__(self, iface): """init window""" QDialog.__init__(self) self.setupUi(self) self.iface = iface self.map = iface.mapCanvas() self.settings = QSettings() self.catalog = None self.catalog_url = None self.context = StaticContext() version = self.context.metadata.get('general', 'version') self.setWindowTitle('MetaSearch %s' % version) self.rubber_band = QgsRubberBand(self.map, True) # True = a polygon self.rubber_band.setColor(QColor(255, 0, 0, 75)) self.rubber_band.setWidth(5) # form inputs self.startfrom = 0 self.maxrecords = 10 self.timeout = 10 self.constraints = [] # Servers tab self.cmbConnectionsServices.activated.connect(self.save_connection) self.cmbConnectionsSearch.activated.connect(self.save_connection) self.btnServerInfo.clicked.connect(self.connection_info) self.btnAddDefault.clicked.connect(self.add_default_connections) self.btnCapabilities.clicked.connect(self.show_xml) self.tabWidget.currentChanged.connect(self.populate_connection_list) # server management buttons self.btnNew.clicked.connect(self.add_connection) self.btnEdit.clicked.connect(self.edit_connection) self.btnDelete.clicked.connect(self.delete_connection) self.btnLoad.clicked.connect(self.load_connections) self.btnSave.clicked.connect(save_connections) # Search tab self.treeRecords.itemSelectionChanged.connect(self.record_clicked) self.treeRecords.itemDoubleClicked.connect(self.show_metadata) self.btnSearch.clicked.connect(self.search) self.leKeywords.returnPressed.connect(self.search) # prevent dialog from closing upon pressing enter self.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False) # launch help from button self.buttonBox.helpRequested.connect(self.help) self.btnCanvasBbox.setAutoDefault(False) self.btnCanvasBbox.clicked.connect(self.set_bbox_from_map) self.btnGlobalBbox.clicked.connect(self.set_bbox_global) # navigation buttons self.btnFirst.clicked.connect(self.navigate) self.btnPrev.clicked.connect(self.navigate) self.btnNext.clicked.connect(self.navigate) self.btnLast.clicked.connect(self.navigate) self.btnAddToWms.clicked.connect(self.add_to_ows) self.btnAddToWfs.clicked.connect(self.add_to_ows) self.btnAddToWcs.clicked.connect(self.add_to_ows) self.btnShowXml.clicked.connect(self.show_xml) # settings self.radioTitleAsk.clicked.connect(self.set_ows_save_title_ask) self.radioTitleNoAsk.clicked.connect(self.set_ows_save_title_no_ask) self.radioTempName.clicked.connect(self.set_ows_save_temp_name) self.manageGui() def manageGui(self): """open window""" self.tabWidget.setCurrentIndex(0) self.populate_connection_list() self.btnCapabilities.setEnabled(False) self.spnRecords.setValue( self.settings.value('/MetaSearch/returnRecords', 10, int)) key = '/MetaSearch/%s' % self.cmbConnectionsSearch.currentText() self.catalog_url = self.settings.value('%s/url' % key) self.set_bbox_global() self.reset_buttons() # get preferred connection save strategy from settings and set it save_strat = self.settings.value('/MetaSearch/ows_save_strategy', 'title_ask') if save_strat == 'temp_name': self.radioTempName.setChecked(True) elif save_strat == 'title_no_ask': self.radioTitleNoAsk.setChecked(True) else: self.radioTitleAsk.setChecked(True) # install proxy handler if specified in QGIS settings self.install_proxy() # Servers tab def populate_connection_list(self): """populate select box with connections""" self.settings.beginGroup('/MetaSearch/') self.cmbConnectionsServices.clear() self.cmbConnectionsServices.addItems(self.settings.childGroups()) self.cmbConnectionsSearch.clear() self.cmbConnectionsSearch.addItems(self.settings.childGroups()) self.settings.endGroup() self.set_connection_list_position() if self.cmbConnectionsServices.count() == 0: # no connections - disable various buttons state_disabled = False self.btnSave.setEnabled(state_disabled) # and start with connection tab open self.tabWidget.setCurrentIndex(1) # tell the user to add services msg = self.tr('No services/connections defined. To get ' 'started with MetaSearch, create a new ' 'connection by clicking \'New\' or click ' '\'Add default services\'.') self.textMetadata.setHtml('<p><h3>%s</h3></p>' % msg) else: # connections - enable various buttons state_disabled = True self.btnServerInfo.setEnabled(state_disabled) self.btnEdit.setEnabled(state_disabled) self.btnDelete.setEnabled(state_disabled) def set_connection_list_position(self): """set the current index to the selected connection""" to_select = self.settings.value('/MetaSearch/selected') conn_count = self.cmbConnectionsServices.count() if conn_count == 0: self.btnDelete.setEnabled(False) self.btnServerInfo.setEnabled(False) self.btnEdit.setEnabled(False) # does to_select exist in cmbConnectionsServices? exists = False for i in range(conn_count): if self.cmbConnectionsServices.itemText(i) == to_select: self.cmbConnectionsServices.setCurrentIndex(i) self.cmbConnectionsSearch.setCurrentIndex(i) exists = True break # If we couldn't find the stored item, but there are some, default # to the last item (this makes some sense when deleting items as it # allows the user to repeatidly click on delete to remove a whole # lot of items) if not exists and conn_count > 0: # If to_select is null, then the selected connection wasn't found # by QSettings, which probably means that this is the first time # the user has used CSWClient, so default to the first in the list # of connetions. Otherwise default to the last. if not to_select: current_index = 0 else: current_index = conn_count - 1 self.cmbConnectionsServices.setCurrentIndex(current_index) self.cmbConnectionsSearch.setCurrentIndex(current_index) def save_connection(self): """save connection""" caller = self.sender().objectName() if caller == 'cmbConnectionsServices': # servers tab current_text = self.cmbConnectionsServices.currentText() elif caller == 'cmbConnectionsSearch': # search tab current_text = self.cmbConnectionsSearch.currentText() self.settings.setValue('/MetaSearch/selected', current_text) key = '/MetaSearch/%s' % current_text if caller == 'cmbConnectionsSearch': # bind to service in search tab self.catalog_url = self.settings.value('%s/url' % key) if caller == 'cmbConnectionsServices': # clear server metadata self.textMetadata.clear() self.btnCapabilities.setEnabled(False) def connection_info(self): """show connection info""" current_text = self.cmbConnectionsServices.currentText() key = '/MetaSearch/%s' % current_text self.catalog_url = self.settings.value('%s/url' % key) # connect to the server if not self._get_csw(): return QApplication.restoreOverrideCursor() if self.catalog: # display service metadata self.btnCapabilities.setEnabled(True) metadata = render_template('en', self.context, self.catalog, 'service_metadata.html') style = QgsApplication.reportStyleSheet() self.textMetadata.clear() self.textMetadata.document().setDefaultStyleSheet(style) self.textMetadata.setHtml(metadata) def add_connection(self): """add new service""" conn_new = NewConnectionDialog() conn_new.setWindowTitle(self.tr('New Catalogue service')) if conn_new.exec_() == QDialog.Accepted: # add to service list self.populate_connection_list() self.textMetadata.clear() def edit_connection(self): """modify existing connection""" current_text = self.cmbConnectionsServices.currentText() url = self.settings.value('/MetaSearch/%s/url' % current_text) conn_edit = NewConnectionDialog(current_text) conn_edit.setWindowTitle(self.tr('Edit Catalogue service')) conn_edit.leName.setText(current_text) conn_edit.leURL.setText(url) if conn_edit.exec_() == QDialog.Accepted: # update service list self.populate_connection_list() def delete_connection(self): """delete connection""" current_text = self.cmbConnectionsServices.currentText() key = '/MetaSearch/%s' % current_text msg = self.tr('Remove service %s?') % current_text result = QMessageBox.information(self, self.tr('Confirm delete'), msg, QMessageBox.Ok | QMessageBox.Cancel) if result == QMessageBox.Ok: # remove service from list self.settings.remove(key) index_to_delete = self.cmbConnectionsServices.currentIndex() self.cmbConnectionsServices.removeItem(index_to_delete) self.cmbConnectionsSearch.removeItem(index_to_delete) self.set_connection_list_position() def load_connections(self): """load services from list""" ManageConnectionsDialog(1).exec_() self.populate_connection_list() def add_default_connections(self): """add default connections""" filename = os.path.join(self.context.ppath, 'resources', 'connections-default.xml') doc = get_connections_from_file(self, filename) if doc is None: return self.settings.beginGroup('/MetaSearch/') keys = self.settings.childGroups() self.settings.endGroup() for server in doc.findall('csw'): name = server.attrib.get('name') # check for duplicates if name in keys: msg = self.tr('%s exists. Overwrite?') % name res = QMessageBox.warning(self, self.tr('Loading connections'), msg, QMessageBox.Yes | QMessageBox.No) if res != QMessageBox.Yes: continue # no dups detected or overwrite is allowed key = '/MetaSearch/%s' % name self.settings.setValue('%s/url' % key, server.attrib.get('url')) self.populate_connection_list() # Settings tab def set_ows_save_title_ask(self): """save ows save strategy as save ows title, ask if duplicate""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_ask') def set_ows_save_title_no_ask(self): """save ows save strategy as save ows title, do NOT ask if duplicate""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'title_no_ask') def set_ows_save_temp_name(self): """save ows save strategy as save with a temporary name""" self.settings.setValue('/MetaSearch/ows_save_strategy', 'temp_name') # Search tab def set_bbox_from_map(self): """set bounding box from map extent""" crs = self.map.mapRenderer().destinationCrs() crsid = int(crs.authid().split(':')[1]) extent = self.map.extent() if crsid != 4326: # reproject to EPSG:4326 src = QgsCoordinateReferenceSystem(crsid) dest = QgsCoordinateReferenceSystem(4326) xform = QgsCoordinateTransform(src, dest) minxy = xform.transform(QgsPoint(extent.xMinimum(), extent.yMinimum())) maxxy = xform.transform(QgsPoint(extent.xMaximum(), extent.yMaximum())) minx, miny = minxy maxx, maxy = maxxy else: # 4326 minx = extent.xMinimum() miny = extent.yMinimum() maxx = extent.xMaximum() maxy = extent.yMaximum() self.leNorth.setText(unicode(maxy)[0:9]) self.leSouth.setText(unicode(miny)[0:9]) self.leWest.setText(unicode(minx)[0:9]) self.leEast.setText(unicode(maxx)[0:9]) def set_bbox_global(self): """set global bounding box""" self.leNorth.setText('90') self.leSouth.setText('-90') self.leWest.setText('-180') self.leEast.setText('180') def search(self): """execute search""" self.catalog = None self.constraints = [] # clear all fields and disable buttons self.lblResults.clear() self.treeRecords.clear() self.reset_buttons() # save some settings self.settings.setValue('/MetaSearch/returnRecords', self.spnRecords.cleanText()) # set current catalogue current_text = self.cmbConnectionsSearch.currentText() key = '/MetaSearch/%s' % current_text self.catalog_url = self.settings.value('%s/url' % key) # start position and number of records to return self.startfrom = 0 self.maxrecords = self.spnRecords.value() # set timeout self.timeout = self.spnTimeout.value() # bbox minx = self.leWest.text() miny = self.leSouth.text() maxx = self.leEast.text() maxy = self.leNorth.text() bbox = [minx, miny, maxx, maxy] # only apply spatial filter if bbox is not global # even for a global bbox, if a spatial filter is applied, then # the CSW server will skip records without a bbox if bbox != ['-180', '-90', '180', '90']: self.constraints.append(BBox(bbox)) # keywords if self.leKeywords.text(): # TODO: handle multiple word searches keywords = self.leKeywords.text() self.constraints.append(PropertyIsLike('csw:AnyText', keywords)) if len(self.constraints) > 1: # exclusive search (a && b) self.constraints = [self.constraints] # build request if not self._get_csw(): return # TODO: allow users to select resources types # to find ('service', 'dataset', etc.) try: self.catalog.getrecords2(constraints=self.constraints, maxrecords=self.maxrecords, esn='full') except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Search error'), self.tr('Search error: %s') % err) return except Exception as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Connection error'), self.tr('Connection error: %s') % err) return if self.catalog.results['matches'] == 0: QApplication.restoreOverrideCursor() self.lblResults.setText(self.tr('0 results')) return QApplication.restoreOverrideCursor() self.display_results() def display_results(self): """display search results""" self.treeRecords.clear() position = self.catalog.results['returned'] + self.startfrom msg = self.tr('Showing %d - %d of %n result(s)', 'number of results', self.catalog.results['matches']) % (self.startfrom + 1, position) self.lblResults.setText(msg) for rec in self.catalog.records: item = QTreeWidgetItem(self.treeRecords) if self.catalog.records[rec].type: item.setText(0, normalize_text(self.catalog.records[rec].type)) else: item.setText(0, 'unknown') if self.catalog.records[rec].title: item.setText(1, normalize_text(self.catalog.records[rec].title)) if self.catalog.records[rec].identifier: set_item_data(item, 'identifier', self.catalog.records[rec].identifier) self.btnShowXml.setEnabled(True) if self.catalog.results["matches"] < self.maxrecords: disabled = False else: disabled = True self.btnFirst.setEnabled(disabled) self.btnPrev.setEnabled(disabled) self.btnNext.setEnabled(disabled) self.btnLast.setEnabled(disabled) def record_clicked(self): """record clicked signal""" # disable only service buttons self.reset_buttons(True, False, False) if not self.treeRecords.selectedItems(): return item = self.treeRecords.currentItem() if not item: return identifier = get_item_data(item, 'identifier') try: record = self.catalog.records[identifier] except KeyError as err: QMessageBox.warning(self, self.tr('Record parsing error'), 'Unable to locate record identifier') return # if the record has a bbox, show a footprint on the map if record.bbox is not None: points = bbox_to_polygon(record.bbox) if points is not None: src = QgsCoordinateReferenceSystem(4326) dst = self.map.mapSettings().destinationCrs() geom = QgsGeometry.fromPolygon(points) if src.postgisSrid() != dst.postgisSrid(): ctr = QgsCoordinateTransform(src, dst) try: geom.transform(ctr) except Exception as err: QMessageBox.warning( self, self.tr('Coordinate Transformation Error'), unicode(err)) self.rubber_band.setToGeometry(geom, None) # figure out if the data is interactive and can be operated on self.find_services(record, item) def find_services(self, record, item): """scan record for WMS/WMTS|WFS|WCS endpoints""" links = record.uris + record.references services = {} for link in links: if 'scheme' in link: link_type = link['scheme'] elif 'protocol' in link: link_type = link['protocol'] else: link_type = None if link_type is not None: link_type = link_type.upper() wmswmst_link_types = map(str.upper, link_types.WMSWMST_LINK_TYPES) wfs_link_types = map(str.upper, link_types.WFS_LINK_TYPES) wcs_link_types = map(str.upper, link_types.WCS_LINK_TYPES) # if the link type exists, and it is one of the acceptable # interactive link types, then set if all([link_type is not None, link_type in wmswmst_link_types + wfs_link_types + wcs_link_types]): if link_type in wmswmst_link_types: services['wms'] = link['url'] self.btnAddToWms.setEnabled(True) if link_type in wfs_link_types: services['wfs'] = link['url'] self.btnAddToWfs.setEnabled(True) if link_type in wcs_link_types: services['wcs'] = link['url'] self.btnAddToWcs.setEnabled(True) set_item_data(item, 'link', json.dumps(services)) def navigate(self): """manage navigation / paging""" caller = self.sender().objectName() if caller == 'btnFirst': self.startfrom = 0 elif caller == 'btnLast': self.startfrom = self.catalog.results['matches'] - self.maxrecords elif caller == 'btnNext': self.startfrom += self.maxrecords if self.startfrom >= self.catalog.results["matches"]: msg = self.tr('End of results. Go to start?') res = QMessageBox.information(self, self.tr('Navigation'), msg, (QMessageBox.Ok | QMessageBox.Cancel)) if res == QMessageBox.Ok: self.startfrom = 0 else: return elif caller == "btnPrev": self.startfrom -= self.maxrecords if self.startfrom <= 0: msg = self.tr('Start of results. Go to end?') res = QMessageBox.information(self, self.tr('Navigation'), msg, (QMessageBox.Ok | QMessageBox.Cancel)) if res == QMessageBox.Ok: self.startfrom = (self.catalog.results['matches'] - self.maxrecords) else: return QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: self.catalog.getrecords2(constraints=self.constraints, maxrecords=self.maxrecords, startposition=self.startfrom, esn='full') except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Search error'), self.tr('Search error: %s') % err) return except Exception as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('Connection error'), self.tr('Connection error: %s') % err) return QApplication.restoreOverrideCursor() self.display_results() def add_to_ows(self): """add to OWS provider connection list""" conn_name_matches = [] item = self.treeRecords.currentItem() if not item: return item_data = json.loads(get_item_data(item, 'link')) caller = self.sender().objectName() # stype = human name,/Qgis/connections-%s,providername if caller == 'btnAddToWms': stype = ['OGC:WMS/OGC:WMTS', 'wms', 'wms'] data_url = item_data['wms'] elif caller == 'btnAddToWfs': stype = ['OGC:WFS', 'wfs', 'WFS'] data_url = item_data['wfs'] elif caller == 'btnAddToWcs': stype = ['OGC:WCS', 'wcs', 'wcs'] data_url = item_data['wcs'] QApplication.restoreOverrideCursor() sname = '%s from MetaSearch' % stype[1] # store connection # check if there is a connection with same name self.settings.beginGroup('/Qgis/connections-%s' % stype[1]) keys = self.settings.childGroups() self.settings.endGroup() for key in keys: if key.startswith(sname): conn_name_matches.append(key) if conn_name_matches: sname = conn_name_matches[-1] # check for duplicates if sname in keys: # duplicate found if self.radioTitleAsk.isChecked(): # ask to overwrite msg = self.tr('Connection %s exists. Overwrite?') % sname res = QMessageBox.warning(self, self.tr('Saving server'), msg, QMessageBox.Yes | QMessageBox.No) if res != QMessageBox.Yes: # assign new name with serial sname = serialize_string(sname) elif self.radioTitleNoAsk.isChecked(): # don't ask to overwrite pass elif self.radioTempName.isChecked(): # use temp name sname = serialize_string(sname) # no dups detected or overwrite is allowed self.settings.beginGroup('/Qgis/connections-%s' % stype[1]) self.settings.setValue('/%s/url' % sname, data_url) self.settings.endGroup() # open provider window ows_provider = QgsProviderRegistry.instance().selectWidget(stype[2], self) service_type = stype[0] # connect dialog signals to iface slots if service_type == 'OGC:WMS/OGC:WMTS': ows_provider.addRasterLayer.connect(self.iface.addRasterLayer) conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections') connect = 'on_btnConnect_clicked' elif service_type == 'OGC:WFS': ows_provider.addWfsLayer.connect(self.iface.mainWindow().addWfsLayer) conn_cmb = ows_provider.findChild(QWidget, 'cmbConnections') connect = 'connectToServer' elif service_type == 'OGC:WCS': ows_provider.addRasterLayer.connect(self.iface.addRasterLayer) conn_cmb = ows_provider.findChild(QWidget, 'mConnectionsComboBox') connect = 'on_mConnectButton_clicked' ows_provider.setModal(False) ows_provider.show() # open provider dialogue against added OWS index = conn_cmb.findText(sname) if index > -1: conn_cmb.setCurrentIndex(index) # only for wfs if service_type == 'OGC:WFS': ows_provider.on_cmbConnections_activated(index) getattr(ows_provider, connect)() def show_metadata(self): """show record metadata""" if not self.treeRecords.selectedItems(): return item = self.treeRecords.currentItem() if not item: return identifier = get_item_data(item, 'identifier') try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) cat = CatalogueServiceWeb(self.catalog_url, timeout=self.timeout) cat.getrecordbyid( [self.catalog.records[identifier].identifier]) except ExceptionReport as err: QApplication.restoreOverrideCursor() QMessageBox.warning(self, self.tr('GetRecords error'), self.tr('Error getting response: %s') % err) return except KeyError as err: QMessageBox.warning(self, self.tr('Record parsing error'), 'Unable to locate record identifier') QApplication.restoreOverrideCursor() return QApplication.restoreOverrideCursor() record = cat.records[identifier] record.xml_url = cat.request crd = RecordDialog() metadata = render_template('en', self.context, record, 'record_metadata_dc.html') style = QgsApplication.reportStyleSheet() crd.textMetadata.document().setDefaultStyleSheet(style) crd.textMetadata.setHtml(metadata) crd.exec_() def show_xml(self): """show XML request / response""" crd = XMLDialog() request_html = highlight_xml(self.context, self.catalog.request) response_html = highlight_xml(self.context, self.catalog.response) style = QgsApplication.reportStyleSheet() crd.txtbrXMLRequest.clear() crd.txtbrXMLResponse.clear() crd.txtbrXMLRequest.document().setDefaultStyleSheet(style) crd.txtbrXMLResponse.document().setDefaultStyleSheet(style) crd.txtbrXMLRequest.setHtml(request_html) crd.txtbrXMLResponse.setHtml(response_html) crd.exec_() def reset_buttons(self, services=True, xml=True, navigation=True): """Convenience function to disable WMS/WMTS|WFS|WCS buttons""" if services: self.btnAddToWms.setEnabled(False) self.btnAddToWfs.setEnabled(False) self.btnAddToWcs.setEnabled(False) if xml: self.btnShowXml.setEnabled(False) if navigation: self.btnFirst.setEnabled(False) self.btnPrev.setEnabled(False) self.btnNext.setEnabled(False) self.btnLast.setEnabled(False) def help(self): """launch help""" open_url(get_help_url()) def reject(self): """back out of dialogue""" QDialog.reject(self) self.rubber_band.reset() def _get_csw(self): """convenience function to init owslib.csw.CatalogueServiceWeb""" # connect to the server try: QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) self.catalog = CatalogueServiceWeb(self.catalog_url, timeout=self.timeout) return True except ExceptionReport as err: msg = self.tr('Error connecting to service: %s') % err except ValueError as err: msg = self.tr('Value Error: %s') % err except Exception as err: msg = self.tr('Unknown Error: %s') % err QMessageBox.warning(self, self.tr('CSW Connection error'), msg) QApplication.restoreOverrideCursor() return False def install_proxy(self): """set proxy if one is set in QGIS network settings""" # initially support HTTP for now if self.settings.value('/proxy/proxyEnabled') == 'true': if self.settings.value('/proxy/proxyType') == 'HttpProxy': ptype = 'http' else: return user = self.settings.value('/proxy/proxyUser') password = self.settings.value('/proxy/proxyPassword') host = self.settings.value('/proxy/proxyHost') port = self.settings.value('/proxy/proxyPort') proxy_up = '' proxy_port = '' if all([user != '', password != '']): proxy_up = '%s:%s@' % (user, password) if port != '': proxy_port = ':%s' % port conn = '%s://%s%s%s' % (ptype, proxy_up, host, proxy_port) install_opener(build_opener(ProxyHandler({ptype: conn})))
class FlowEstimatorDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent=None): """Constructor.""" super(FlowEstimatorDialog, 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 #QDialog.__init__(self, None, Qt.WindowStaysOnTopHint) self.iface = iface self.setupUi(self) self.btnOk = self.buttonBox.button(QDialogButtonBox.Save) #ajh: it seems this wasn't working, so I have changed from a stock OK button to a stock Save button #self.btnOk.setText("Save Data") self.btnClose = self.buttonBox.button(QDialogButtonBox.Close) self.btnBrowse.clicked.connect(self.writeDirName) self.btnLoadTXT.clicked.connect(self.loadTxt) # ajh trying to make it work #self.inputFile.textChanged.connect(self.run) self.btnSampleLine.setEnabled(False) self.btnSampleSlope.setEnabled(False) self.calcType = 'Trap' # add shortcut keys to zoom in the documentation tab, because why not? # pinch zoom and crtl+scroll already work by default, although zooming is impossible if a font size is set # I guess it would be nice to add zooming to the context menu too, or provide buttons QShortcut(QKeySequence('Ctrl++'), self.textBrowser, self.textBrowser.zoomIn) QShortcut(QKeySequence('Ctrl+-'), self.textBrowser, self.textBrowser.zoomOut) # add matplotlib figure to dialog self.figure = Figure() # this controls the size of the whole dialog; if we get resizing working again I guess we need to add `, forward=True` self.figure.set_size_inches(6, 2.8) self.axes = self.figure.add_subplot(111) self.figure.subplots_adjust(left=.12, bottom=0.15, right=.75, top=.9, wspace=None, hspace=.2) self.mplCanvas = FigureCanvas(self.figure) #self.widgetPlotToolbar = NavigationToolbar(self.mplCanvas, self.widgetPlot) #lstActions = self.widgetPlotToolbar.actions() #self.widgetPlotToolbar.removeAction(lstActions[7]) self.vLayout.addWidget(self.mplCanvas) self.vLayout.minimumSize() #self.vLayout.addWidget(self.widgetPlotToolbar) # ajh: change the colours; perhaps we could use a stylesheet instead # ajh: can change the colour outside the actual graph like this #self.figure.patch.set_facecolor("blue") # ajh: default transparency is 1.0; good since otherwise the exported graphs have illegible axes in Windows explorer preview due to being fully transparent, and in QGIS if using a dark theme. #self.figure.patch.set_alpha(0.5) # ajh: patch (fill) is shown by default; good as per comment above #self.figure.patch.set_visible(False) # ajh: can change the background colour of the main plot like this: #self.axes.set_facecolor("#eafff5") # ajh: these don't seem to do anything #self.axes.patch.set_facecolor("#eafff5") #self.axes.patch.set_visible(True) #self.axes.patch.set_alpha(0.0) # and configure matplotlib params # rcParams["font.serif"] = "Verdana, Arial, Liberation Serif" # rcParams["font.sans-serif"] = "Tahoma, Arial, Liberation Sans" # rcParams["font.cursive"] = "Courier New, Arial, Liberation Sans" # rcParams["font.fantasy"] = "Comic Sans MS, Arial, Liberation Sans" # rcParams["font.monospace"] = "Courier New, Liberation Mono" # #print self.cbDEM.changeEvent self.depth.valueChanged.connect(self.run) self.botWidth.valueChanged.connect(self.run) self.leftSS.valueChanged.connect(self.run) self.rightSS.valueChanged.connect(self.run) self.n.valueChanged.connect(self.run) self.slope.valueChanged.connect(self.run) self.cbWSE.valueChanged.connect(self.run) self.ft.clicked.connect(self.run) self.m.clicked.connect(self.run) self.cbUDwse.valueChanged.connect(self.run) # ajh: this doesn't fix it #self.btnRefresh.clicked.connect(self.run) # it seems nothing has the keyboard focus initially unless we set it manually self.tabWidget.setFocus() self.manageGui() # ajh: I thought this would work around the crashes, but it doesn't work properly - it only sets a maximum size (almost - it can still be made slightly taller!) #self.setFixedSize(self.size()) # even this doesn't help #self.setMinimumSize(self.size()) self.btnSampleLine.clicked.connect(self.sampleLine) self.btnSampleSlope.clicked.connect(self.sampleSlope) def manageGui(self): # fix_print_with_import print('manageGui') self.cbDEM.clear() if utils.getRasterLayerNames(): self.cbDEM.addItems(utils.getRasterLayerNames()) self.btnSampleLine.setEnabled(True) self.btnSampleSlope.setEnabled(True) self.run() # def refreshPlot(self): # self.axes.clear() def plotter(self): R, area, topWidth, Q, v, depth, xGround, yGround, yGround0, xWater, yWater, yWater0 = self.args self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(xGround, yGround, 'k') #self.axes.fill_between(xGround, yGround, yGround0, where=yGround>yGround0, facecolor='0.9', interpolate=True) if Q != 0: self.axes.plot(xWater, yWater, 'blue') self.axes.fill_between(xWater, yWater, yWater0, where=yWater >= yWater0, facecolor='blue', interpolate=True, alpha=0.1) if self.calcType == 'DEM': self.outText = 'INPUT\n\nSlope: {7:.3f}\nRoughness: {8:.3f}\nWSE: {9:.2f} {5}\n\nCALCULATED\n\nR: {0:.2f} {5}\nArea: {1:,.2f} {5}$^2$\nTop Width: {2:.2f} {5}\nDepth: {6:,.2f} {5}\nQ: {3:,.3f} {5}$^3$/s\nVelocity {4:,.1f} {5}/s'.format( R, area, topWidth, Q, v, self.units, depth, self.slope.value(), self.n.value(), self.cbWSE.value()) elif self.calcType == 'UD': self.outText = 'INPUT\n\nSlope: {7:.3f}\nRoughness: {8:.3f}\nWSE: {9:.2f} {5}\n\nCALCULATED\n\nR: {0:.2f} {5}\nArea: {1:,.2f} {5}$^2$\nTop Width: {2:.2f} {5}\nDepth: {6:,.2f} {5}\nQ: {3:,.3f} {5}$^3$/s\nVelocity {4:,.1f} {5}/s'.format( R, area, topWidth, Q, v, self.units, depth, self.slope.value(), self.n.value(), self.cbUDwse.value()) else: self.outText = 'INPUT\n\nSlope: {7:.3f}\nRoughness: {8:.3f}\nDepth: {9:.2f} {5}\n\nCALCULATED\n\nR: {0:.2f} {5}\nArea: {1:,.2f} {5}$^2$\nTop Width: {2:.2f} {5}\nDepth: {6:,.2f} {5}\nQ: {3:,.3f} {5}$^3$/s\nVelocity {4:,.1f} {5}/s'.format( R, area, topWidth, Q, v, self.units, depth, self.slope.value(), self.n.value(), self.depth.value()) self.axes.set_xlabel('Station, ' + self.units) self.axes.set_ylabel('Elevation, ' + self.units) self.axes.set_title('Cross Section') #self.axes.set_ylim(bottom=0) #self.axes.show() #print self.outText self.refreshPlotText() def refreshPlotText(self): self.axes.annotate(self.outText, xy=(.76, 0.17), xycoords='figure fraction') # enable mouseover coordinate display if mplcursors is available # using click coordinate display instead could be desirable, to output it when saving results, but we currently recalculate when saving, which clears it if MPLCURSORS == "installed": mplcursors.cursor(ground, hover=True) # we can do this as well, but it is a bit weird interacting with both of them # mplcursors.cursor(water, hover=True) #at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") #self.axes.add_artist(at) self.mplCanvas.draw() def run(self): if self.ft.isChecked(): self.units = 'ft' else: self.units = 'm' if self.tabWidget.currentIndex() == 0: # fix_print_with_import print('calc trap channel') self.calcType = 'Trap' self.args = flowEstimator(self.depth.value(), self.n.value(), self.slope.value(), widthBottom=self.botWidth.value(), rightSS=self.rightSS.value(), leftSS=self.leftSS.value(), units=self.units) self.figure.patch.set_facecolor("white") self.plotter() elif self.tabWidget.currentIndex() == 1: try: self.calcType = 'DEM' # print self.cbWSE.value(), self.n.value(), self.slope.value(), self.staElev, self.units self.args = flowEstimator(self.cbWSE.value(), self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) self.figure.patch.set_facecolor("white") self.plotter() except: self.figure.patch.set_facecolor("red") #doesn't seem to do anything: #self.mplCanvas.setEnabled(False) #don't think this does anything?: #self.axes.clear() #this doesn't help #self.plotter() self.mplCanvas.draw() else: try: self.calcType = 'UD' #print 'self.cbUDwse.value(), self.n.value(), self.slope.value(), staElev = self.staElev, units = self.units' self.args = flowEstimator(self.cbUDwse.value(), self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) self.figure.patch.set_facecolor("white") self.plotter() except: self.figure.patch.set_facecolor("red") #doesn't seem to do anything: #self.mplCanvas.setEnabled(False) #don't think this does anything?: #self.axes.clear() #this doesn't help #self.plotter() self.mplCanvas.draw() def sampleLine(self): self.hide() self.iface.mainWindow().activateWindow() #ajh don't need this if we are doing hide and show; and it is a problem if the tool is deactivated #self.btnSampleLine.setEnabled(False) self.sampleBtnCode = 'sampleLine' self.rubberBand() def sampleSlope(self): self.hide() self.iface.mainWindow().activateWindow() #ajh don't need this if we are doing hide and show; and it is a problem if the tool is deactivated #self.btnSampleSlope.setEnabled(False) self.sampleBtnCode = 'sampleSlope' self.rubberBand() #============================================================================== # START rubberband and related functions from # https://github.com/etiennesky/profiletool #============================================================================== def rubberBand(self): # fix_print_with_import print('rubberband ') self.canvas = self.iface.mapCanvas() #Init class variables if self.sampleBtnCode == 'sampleLine': self.tool = ProfiletoolMapTool( self.canvas, self.btnSampleLine) #the mouselistener else: self.tool = ProfiletoolMapTool( self.canvas, self.btnSampleSlope) #the mouselistener self.pointstoDraw = None #Polyline in mapcanvas CRS analysed self.dblclktemp = False #enable distinction between leftclick and doubleclick # ajh: we would need to do more work to support selectionmethod = 1 self.selectionmethod = 0 #The selection method defined in option self.saveTool = self.canvas.mapTool( ) #Save the standard mapttool for restoring it at the end self.textquit0 = "Click for polyline and double click to end (right click to cancel)" self.textquit1 = "Select the polyline in a vector layer (Right click to quit)" #Listeners of mouse self.connectTool() #init the mouse listener comportement and save the classic to restore it on quit self.canvas.setMapTool(self.tool) #init the temp layer where the polyline is draw self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) self.rubberband.setWidth(2) if self.sampleBtnCode == 'sampleLine': color = Qt.red else: color = Qt.blue self.rubberband.setColor(QColor(color)) #init the table where is saved the polyline self.pointstoDraw = [] self.pointstoCal = [] self.lastClicked = [[-9999999999.9, 9999999999.9]] # The last valid line we drew to create a free-hand profile self.lastFreeHandPoints = [] #Help about what doing if self.selectionmethod == 0: self.iface.mainWindow().statusBar().showMessage(self.textquit0) elif self.selectionmethod == 1: self.iface.mainWindow().statusBar().showMessage(self.textquit1) #************************************* Mouse listener actions *********************************************** def moved(self, position): #draw the polyline on the temp layer (rubberband) #print 'moved' if self.selectionmethod == 0: if len(self.pointstoDraw) > 0: #Get mouse coords mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) try: self.rubberband.reset(QgsWkbTypes.LineGeometry) except: self.rubberband.reset(QGis.Line) for i in range(0, len(self.pointstoDraw)): self.rubberband.addPoint( QgsPointXY(self.pointstoDraw[i][0], self.pointstoDraw[i][1])) self.rubberband.addPoint(QgsPointXY(mapPos.x(), mapPos.y())) # if self.selectionmethod == 1: # return def tooldeactivated(self): self.rubberband.reset(self.polygon) self.iface.mainWindow().statusBar().showMessage("") self.show() def rightClicked(self, position): #used to quit the current action # fix_print_with_import print('rightclicked') if self.selectionmethod == 0: if len(self.pointstoDraw) > 0: self.pointstoDraw = [] self.pointstoCal = [] self.rubberband.reset(self.polygon) else: self.cleaning() # ajh2: don't actually need this now, as we hide the window instead of deactivating the button # ajh: need this otherwise the plugin needs to be restarted to reenable the button #if self.sampleBtnCode == 'sampleLine': # self.btnSampleLine.setEnabled(True) #else : # self.btnSampleSlope.setEnabled(True) # ajh2: don't seem to need this now # ajh: need to raise the window #self.activateWindow() def leftClicked(self, position): #Add point to analyse # fix_print_with_import print('leftclicked') mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoints = [[mapPos.x(), mapPos.y()]] if self.selectionmethod == 0: if newPoints == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointstoDraw) == 0: self.rubberband.reset(self.polygon) self.pointstoDraw += newPoints def doubleClicked(self, position): # fix_print_with_import print('doubleclicked') # ajh: doing show first avoids problems with dialog not showing in some cases after running slope estimator # and it also means the user can see the graph before deciding whether to accept the slope self.show() if self.selectionmethod == 0: #Validation of line mapPos = self.canvas.getCoordinateTransform().toMapCoordinates( position["x"], position["y"]) newPoints = [[mapPos.x(), mapPos.y()]] self.pointstoDraw += newPoints #launch analyses self.iface.mainWindow().statusBar().showMessage( str(self.pointstoDraw)) if self.sampleBtnCode == 'sampleLine': self.staElev, error = self.doRubberbandProfile() if error: pass #ajh it would be good to restart the selection again after an error else: self.doIrregularProfileFlowEstimator() #ajh don't need this if we are doing hide and show #self.btnSampleLine.setEnabled(True) self.deactivate() else: staElev, error = self.doRubberbandProfile() if error: pass #ajh it would be good to restart the selection again after an error else: self.doRubberbandSlopeEstimator(staElev) #ajh don't need this if we are doing hide and show #self.btnSampleSlope.setEnabled(True) self.deactivate() #Reset self.lastFreeHandPoints = self.pointstoDraw self.pointstoDraw = [] #temp point to distinct leftclick and dbleclick self.dblclktemp = newPoints self.iface.mainWindow().statusBar().showMessage("") # ajh: trying to make something like this work: #self.iface.mainWindow.setWindowState(self.iface.mainWindow.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) #self.iface.mainWindow.raise_() #self.iface.mainWindow.show() # ajh: this is (really) it; needs a hide first... but we are moving it to the start as discussed there #self.show() # ajh: thought this just wasn't working on windows as per # https://stackoverflow.com/questions/22815608/how-to-find-the-active-pyqt-window-and-bring-it-to-the-front # see https://forum.qt.io/topic/1939/activatewindow-does-not-send-window-to-front/11 #self.iface.mainWindow.activateWindow() # but actually, this is the solution: #self.activateWindow() return ###*********************************************** def connectTool(self): # fix_print_with_import print('connecting') self.tool.moved.connect(self.moved) self.tool.rightClicked.connect(self.rightClicked) self.tool.leftClicked.connect(self.leftClicked) self.tool.doubleClicked.connect(self.doubleClicked) self.tool.deactivated.connect(self.tooldeactivated) def deactivate(self): #enable clean exit of the plugin self.cleaning() try: self.tool.moved.disconnect(self.moved) self.tool.leftClicked.disconnect(self.leftClicked) self.tool.rightClicked.disconnect(self.rightClicked) self.tool.doubleClicked.disconnect(self.doubleClicked) self.tool.deactivated.disconnect(self.tooldeactivated) except: pass # self.rubberband.reset(self.polygon) # self.iface.mainWindow().statusBar().showMessage("") # self.depth.setEnabled(True) # self.botWidth.setEnabled(True) # self.leftSS.setEnabled(True) # self.rightSS.setEnabled(True) # self.n.setEnabled(True) # self.slope.setEnabled(True) # self.cbWSE.setEnabled(True) # self.ft.setEnabled(True) # self.m.setEnabled(True) # self.cbDEM.setEnabled(True) def cleaning(self): #used on right click and deactivate self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.saveTool) self.rubberband.reset(self.polygon) self.iface.mainWindow().statusBar().showMessage( "" ) # ajh: I guess there might have been a statusBar message associated with the saveTool which we should restore #============================================================================== # END rubberband and related functions from # https://github.com/etiennesky/profiletool #============================================================================== def doRubberbandProfile(self): layerString = self.cbDEM.currentText() layer = utils.getRasterLayerByName(' '.join( layerString.split(' ')[:-1])) try: if layer.isValid(): self.xRes = layer.rasterUnitsPerPixelX() except: QMessageBox.warning(self, 'Error', 'Selected DEM layer is missing') return [None, 'error'] line = LineString(self.pointstoDraw[:-1]) xyzdList = utils.elevationSampler(line, self.xRes, layer) sta = xyzdList[-1] elev = xyzdList[-2] staElev = np.array(list(zip(sta, elev))) try: np.isnan(np.sum(staElev[:, 1])) return [staElev, None] except: QMessageBox.warning(self, 'Error', 'Sampled line not within bounds of DEM') # ajh: don't think we need this #self.cleaning() return [staElev, 'error'] def doIrregularProfileFlowEstimator(self): thalweig = self.staElev[np.where( self.staElev[:, 1] == np.min(self.staElev[:, 1]))] thalweigX = thalweig[:, 0][0] minElev = thalweig[:, 1][0] + .01 try: lbMaxEl = self.staElev[np.where( self.staElev[:, 0] > thalweigX)][:, 1].max() except: QMessageBox.warning(self, 'Error', 'Channel not found') # ajh: don't think we need this #self.deactivate() return try: rbMaxEl = self.staElev[np.where( self.staElev[:, 0] < thalweigX)][:, 1].max() except: QMessageBox.warning(self, 'Error', 'Channel not found') # ajh: don't think we need this #self.deactivate() return maxElev = np.array([lbMaxEl, rbMaxEl]).min( ) - .001 # ajh: let the user set WSE up to 1mm (if units in m) below the crest WSE = maxElev WSE = (self.staElev[:, 1].max() - self.staElev[:, 1].min()) / 2. + self.staElev[:, 1].min() self.cbWSE.setValue(WSE) self.cbWSE.setMinimum(minElev) self.cbWSE.setMaximum(maxElev) self.cbUDwse.setValue(WSE) self.cbUDwse.setMinimum(minElev) self.cbUDwse.setMaximum(maxElev) # ajh: doing it like this might be beneficial if switch from WSE to UD (or vice versa) and then fail to load a section succcessfully # as it was it just meant we had a section with the wrong minimum and maximum values if we switched, loaded a section successfully, and then switched back # if we are going to do it this way we should store two different sections (one for each tab) and reload when we switch tabs # if self.tabWidget.currentIndex() == 1: # self.cbWSE.setValue(WSE) # self.cbWSE.setMinimum(minElev) # self.cbWSE.setMaximum(maxElev) # elif self.tabWidget.currentIndex() == 2: # self.cbUDwse.setValue(WSE) # self.cbUDwse.setMinimum(minElev) # self.cbUDwse.setMaximum(maxElev) # else: # return # ajh: I don't think this was doing anything #self.run() def doRubberbandSlopeEstimator(self, staElev): slope = -(staElev[:, 1][-1] - staElev[:, 1][0]) / staElev[:, 0][-1] # fix_print_with_import print(slope) self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(staElev[:, 0], staElev[:, 1], 'k', label='Sampled DEM') x = np.array([staElev[0, 0], staElev[-1, 0]]) y = np.array([staElev[0, 1], staElev[-1, 1]]) self.axes.plot(x, y, label='Slope') self.axes.set_xlabel('Station, ' + self.units) self.axes.set_ylabel('Elevation, ' + self.units) #self.axes.set_title('DEM Derived Slope = '+str(slope)) self.axes.set_title('DEM Derived Slope = ' + str(slope.astype('U8'))) self.axes.legend() self.mplCanvas.draw() if slope <= 0: QMessageBox.warning( self, 'Error', 'Negative or zero slope\nPlease check sampled area\n\nWater flows downhill you know!' ) # fix_print_with_import print('error: negative slope') else: reply = QMessageBox.question( self, 'Message', 'DEM Derived Slope is {}\nWould you like to use this value?'. format(str(slope.astype('U8'))), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.slope.setValue(slope) else: pass def writeDirName(self): self.outputDir.clear() self.dirName = QFileDialog.getExistingDirectory( self, 'Select Output Directory') self.outputDir.setText(self.dirName) def loadTxt(self): try: filePath, __ = QFileDialog.getOpenFileNameAndFilter( self, 'Select tab or space delimited text file containing station and elevation data' ) # QGIS2 (using this rather than getOpenFileName will avoid opening a dialog twice) except: filePath, __ = QFileDialog.getOpenFileName( self, 'Select tab or space delimited text file containing station and elevation data' ) # fix_print_with_import print(filePath) try: self.staElev = np.loadtxt(filePath) self.inputFile.setText(filePath) self.calcType = 'UD' self.doIrregularProfileFlowEstimator() except: if (filePath == ('')): # null string for cancel return QMessageBox.warning( self, 'Error', 'Please check that the text file is space or tab delimited and does not contain header information' ) def accept(self): # recalculate in case save has been hit twice (otherwise instead of saving the cross-section png it saves a second copy of the rating curve). self.run() # assign results to numpy array for quick csv dump outPath = self.outputDir.text() home = os.path.expanduser("~") if outPath == '': outPath = os.path.join(home, 'Desktop', 'QGISFlowEstimatorFiles') self.outputDir.setText(outPath) # Note that in Python 3.2+ we will be able to just do: os.makedirs("path/to/directory", exist_ok=True) if not os.path.exists(outPath): os.makedirs(outPath) fileName = outPath + '/FlowEstimatorResults.txt' # ajh I think we've fixed the file/folder locking problem on windows by not doing chdir to outPath # keeping these old comments just in case: # one way to prevent it may be to open like this: # outFile = open(fileName, 'w', False) # another way may be to do outFile = None after closing outFile = open(fileName, 'w') outHeader = '*' * 20 + '\nFlow Estimator - A QGIS plugin\nEstimates uniform, steady flow in a channel using Mannings equation\n' + '*' * 20 if self.calcType == 'DEM': try: proj4 = utils.getRasterLayerByName( self.cbDEM.currentText().split( ' EPSG')[0]).crs().toProj4() except: proj4 = "Unknown" outHeader += '\n' * 5 + 'Type:\tCross Section from DEM\nUnits:\t{0}\nDEM Layer:\t{1}\nProjection (Proj4 format):\t{2}\nChannel Slope:\t{3:.06f}\nMannings n:\t{4:.02f}\n\n\n\nstation\televation\n'.format( self.units, self.cbDEM.currentText(), proj4, self.slope.value(), self.n.value()) outFile.write(outHeader) np.savetxt(outFile, self.staElev, fmt='%.3f', delimiter='\t') wseMax = self.cbWSE.value() wseMin = self.cbWSE.minimum() elif self.calcType == 'UD': outHeader += '\n' * 5 + 'Type:\tUser Defined Cross Section\nUnits:\t{0}\nChannel Slope:\t{1:.06f}\nMannings n:\t{2:.02f}\n\n\n\nstation\televation\n'.format( self.units, self.slope.value(), self.n.value()) outFile.write(outHeader) np.savetxt(outFile, self.staElev, fmt='%.3f', delimiter='\t') wseMax = self.cbUDwse.value() wseMin = self.cbUDwse.minimum() else: outHeader += '\n' * 5 + 'Type:\tTrapezoidal Channel\nUnits:\t{0}\nChannel Slope:\t{1:.06f}\nMannings n:\t{2:.02f}\nBottom Width:\t{3:.02f}\nRight Side Slope:\t{4:.02f}\nLeft Side Slope:\t{5:.02f}\n'.format( self.units, self.slope.value(), self.n.value(), self.botWidth.value(), self.rightSS.value(), self.leftSS.value()) outFile.write(outHeader) wseMax = self.depth.value() wseMin = 0.001 self.mplCanvas.print_figure(outPath + '/FlowEstimatorResultsXSFigure') outHeader = '\n\n\n\n\n\n\nwater surface elevation\tflow\tvelocity\tR\tarea\ttop width\tdepth\n' outFile.write(outHeader) ###do loop here step = 0.1 wseList = [] qList = [] for wse in utils.frange(wseMin, wseMax, step): if self.calcType == 'DEM' or self.calcType == 'UD': args = flowEstimator(wse, self.n.value(), self.slope.value(), staElev=self.staElev, units=self.units) else: args = flowEstimator(wse, self.n.value(), self.slope.value(), widthBottom=self.botWidth.value(), rightSS=self.rightSS.value(), leftSS=self.leftSS.value(), units=self.units) R, area, topWidth, Q, v, depth, xGround, yGround, yGround0, xWater, yWater, yWater0 = args data = '{0}\t{1:.02f}\t{2:.02f}\t{3:.02f}\t{4:.02f}\t{5:.02f}\t{6:.02f}\n'.format( wse, Q, v, R, area, topWidth, depth) outFile.write(data) wseList.append(wse) qList.append(Q) self.axes.clear() formatter = ScalarFormatter(useOffset=False) self.axes.yaxis.set_major_formatter(formatter) self.axes.plot(qList, wseList, 'k', label='Rating Curve') self.axes.set_ylabel('Water Surface Elevation, ' + self.units) self.axes.set_xlabel('Discharge, {0}$^3$/s'.format(self.units)) self.axes.set_title('Rating Curve') self.axes.grid() self.mplCanvas.draw() self.mplCanvas.print_figure(outPath + '/FlowEstimatorRatingCurve') outFile.close() #ajh this may help force the file lock to be released outFile = None self.iface.messageBar().pushMessage( "Flow Estimator", 'Output files saved to {}'.format(outPath), duration=30)
class teamqgisDock(QDockWidget, Ui_teamqgis): dockRemoved = pyqtSignal(str) def __init__(self, iface, layer, currentFeature): self.iface = iface self.layer = layer self.proj = QgsProject.instance() self.renderer = self.iface.mapCanvas().mapRenderer() self.settings = MySettings() QDockWidget.__init__(self) self.setupUi(self) # Track attr warnings so they are not repeated for multiple items self.warned_attr_values = [] self.setWindowTitle("teamqgis: %s" % layer.name()) if layer.hasGeometryType() is False: self.panCheck.setChecked(False) self.panCheck.setEnabled(False) self.scaleCheck.setChecked(False) self.scaleCheck.setEnabled(False) self.previousButton.setArrowType(Qt.LeftArrow) self.nextButton.setArrowType(Qt.RightArrow) icon = QIcon(":/plugins/teamqgis/icons/openform.svg") self.editFormButton.setIcon(icon) # actions icon = QIcon(":/plugins/teamqgis/icons/action.svg") self.actionButton.setIcon(icon) self.attrAction = layer.actions() actions = [self.attrAction[i] for i in range(self.attrAction.size())] preferredAction = layer.customProperty("teamqgisPreferedAction", "") if preferredAction not in actions: dfltAction = self.attrAction.defaultAction() if dfltAction > len(actions): preferredAction = self.attrAction[dfltAction].name() preferredActionFound = False for i, action in enumerate(actions): qAction = QAction(QIcon(":/plugins/teamqgis/icons/action.svg"), action.name(), self) qAction.triggered.connect(lambda: self.doAction(i)) self.actionButton.addAction(qAction) if action.name() == preferredAction: self.actionButton.setDefaultAction(qAction) preferredActionFound = True if len(actions) == 0: self.actionButton.setEnabled(False) elif not preferredActionFound: self.actionButton.setDefaultAction(self.actionButton.actions()[0]) self.nameComboBoxes = [self.fieldOneNameComboBox, self.fieldTwoNameComboBox, self.fieldThreeNameComboBox] self.valueComboBoxes = [self.fieldOneValueComboBox, self.fieldTwoValueComboBox, self.fieldThreeValueComboBox] self.updateNameComboBoxes() # Restore saved nameComboBox current indices if they exist for nameComboBox in self.nameComboBoxes: fieldName = self.layer.customProperty("teamqgis" + nameComboBox.objectName()) if fieldName != None: nameComboBox.setCurrentIndex(nameComboBox.findText(fieldName)) self.rubber = QgsRubberBand(self.iface.mapCanvas()) self.selectionChanged() if currentFeature == self.listCombo.currentIndex(): self.on_listCombo_currentIndexChanged(currentFeature) else: self.listCombo.setCurrentIndex(currentFeature) self.layer.layerDeleted.connect(self.close) self.layer.selectionChanged.connect(self.selectionChanged) self.layer.layerModified.connect(self.layerChanged) self.layer.editingStopped.connect(self.editingStopped) self.layer.editingStarted.connect(self.editingStarted) QObject.connect(self.proj, SIGNAL("allowedClassesChanged()"), self.updateValueComboBoxes) def updateNameComboBoxes(self): fieldNameMap = self.layer.dataProvider().fieldNameMap() allFields = fieldNameMap.keys() if 'ID' in allFields: allFields.remove('ID') if 'FID' in allFields: allFields.remove('FID') for nameComboBox in self.nameComboBoxes: nameComboBox.clear() nameComboBox.addItems(allFields) def updateValueComboBoxes(self): feature = self.getCurrentItem() for (valueComboBox, nameComboBox) in zip(self.valueComboBoxes, self.nameComboBoxes): valueComboBox.clear() allowedClasses, hasAllowedClasses = self.proj.readListEntry("teamqgis", "allowedClasses") if hasAllowedClasses: valueComboBox.addItems(allowedClasses) attr_value = str(feature[nameComboBox.currentText()]) if (allowedClasses == None) or (attr_value not in allowedClasses) and (attr_value not in self.warned_attr_values): self.iface.messageBar().pushMessage("Class name not in allowed class list", 'Assign an allowed class or add "%s" to allowed class list'%attr_value, level=QgsMessageBar.WARNING, duration=3) self.warned_attr_values.append(attr_value) valueComboBox.addItem(attr_value) valueComboBox.setCurrentIndex(valueComboBox.findText(attr_value)) def setRubber(self, feature): self.rubber.setColor(self.settings.value("rubberColor")) self.rubber.setWidth(self.settings.value("rubberWidth")) ##self.rubber.setLineStyle(Qt.DotLine) self.rubber.setBrushStyle(Qt.NoBrush) self.rubber.setToGeometry(feature.geometry(), self.layer) def closeEvent(self, e): self.rubber.reset() self.layer.layerDeleted.disconnect(self.close) self.layer.selectionChanged.disconnect(self.selectionChanged) self.layer.layerModified.disconnect(self.layerChanged) self.layer.editingStopped.disconnect(self.editingStopped) self.layer.editingStarted.disconnect(self.editingStarted) if self.settings.value("saveSelectionInProject"): self.layer.setCustomProperty("teamqgisSelection", repr([])) self.dockRemoved.emit(self.layer.id()) def selectionChanged(self): self.cleanBrowserFields() self.rubber.reset() nItems = self.layer.selectedFeatureCount() if nItems < 1: self.close() self.layer.emit(SIGNAL("browserNoItem()")) return self.browseFrame.setEnabled(True) self.subset = self.layer.selectedFeaturesIds() if self.settings.value("saveSelectionInProject"): self.layer.setCustomProperty("teamqgisSelection", repr(self.subset)) for fid in self.subset: self.listCombo.addItem("%u" % fid) self.setRubber(self.getCurrentItem()) self.updateValueComboBoxes() def layerChanged(self): self.applyChangesButton.setEnabled(True) def editingStarted(self): for valueComboBox in self.valueComboBoxes: valueComboBox.setEnabled(True) self.translateRightButton.setEnabled(True) self.translateLeftButton.setEnabled(True) self.translateUpButton.setEnabled(True) self.translateDownButton.setEnabled(True) self.editFormButton.setDown(True) def editingStopped(self): self.applyChangesButton.setEnabled(False) for valueComboBox in self.valueComboBoxes: valueComboBox.setEnabled(False) self.translateRightButton.setEnabled(False) self.translateLeftButton.setEnabled(False) self.translateUpButton.setEnabled(False) self.translateDownButton.setEnabled(False) self.editFormButton.setDown(False) def cleanBrowserFields(self): self.currentPosLabel.setText('0/0') self.listCombo.clear() def panScaleToItem(self, feature): if self.panCheck.isChecked() is False: return featBobo = feature.geometry().boundingBox() # if scaling and bobo has width and height (i.e. not a point) if self.scaleCheck.isChecked() and featBobo.width() != 0 and featBobo.height() != 0: featBobo.scale(self.settings.value("scale")) ul = self.renderer.layerToMapCoordinates(self.layer, QgsPoint(featBobo.xMinimum(), featBobo.yMaximum())) ur = self.renderer.layerToMapCoordinates(self.layer, QgsPoint(featBobo.xMaximum(), featBobo.yMaximum())) ll = self.renderer.layerToMapCoordinates(self.layer, QgsPoint(featBobo.xMinimum(), featBobo.yMinimum())) lr = self.renderer.layerToMapCoordinates(self.layer, QgsPoint(featBobo.xMaximum(), featBobo.yMinimum())) x = (ul.x(), ur.x(), ll.x(), lr.x()) y = (ul.y(), ur.y(), ll.y(), lr.y()) x0 = min(x) y0 = min(y) x1 = max(x) y1 = max(y) else: panTo = self.renderer.layerToMapCoordinates(self.layer, featBobo.center()) mapBobo = self.iface.mapCanvas().extent() xshift = panTo.x() - mapBobo.center().x() yshift = panTo.y() - mapBobo.center().y() x0 = mapBobo.xMinimum() + xshift y0 = mapBobo.yMinimum() + yshift x1 = mapBobo.xMaximum() + xshift y1 = mapBobo.yMaximum() + yshift self.iface.mapCanvas().setExtent(QgsRectangle(x0, y0, x1, y1)) self.iface.mapCanvas().refresh() def getCurrentItem(self): i = self.listCombo.currentIndex() if i == -1: return None f = QgsFeature() if self.layer.getFeatures(QgsFeatureRequest().setFilterFid(self.subset[i])).nextFeature(f): return f else: raise NameError("feature not found") def doAction(self, i): f = self.getCurrentItem() self.actionButton.setDefaultAction(self.actionButton.actions()[i]) self.layer.setCustomProperty("teamqgisPreferedAction", self.attrAction[i].name()) self.attrAction.doActionFeature(i, f) def doTranslate(self, trans): # Based on the "doaffine" function in the qgsAffine plugin if (self.layer.geometryType() == 2): start=1 else: start=0 if (not self.layer.isEditable()): self.iface.messageBar().pushMessage("Layer not in edit mode", 'Select a vector layer and choose "Toggle Editing"', level=QgsMessageBar.WARNING) else: feature = self.getCurrentItem() result = feature.geometry() i = start vertex = result.vertexAt(i) fid = feature.id() while (vertex != QgsPoint(0, 0)): newx = vertex.x() + trans[0] * float(self.settings.value("xres")) newy = vertex.y() + trans[1] * float(self.settings.value("yres")) result.moveVertex(newx, newy, i) i += 1 vertex = result.vertexAt(i) self.layer.changeGeometry(fid, result) self.iface.mapCanvas().refresh() self.rubber.reset() self.setRubber(feature) def changeAttribute(self, i, fieldNameComboBox, fieldValueComboBox): fieldValueComboBox.setCurrentIndex(i) feature = self.getCurrentItem() attr_index = self.layer.dataProvider().fieldNameMap()[fieldNameComboBox.currentText()] self.layer.changeAttributeValue(feature.id(), attr_index, fieldValueComboBox.currentText()) self.iface.mapCanvas().refresh() self.updateValueComboBoxes() def nameComboBox_activated(self, i, nameComboBox): self.layer.setCustomProperty('teamqgis' + nameComboBox.objectName(), nameComboBox.currentText()) @pyqtSlot(name="on_previousButton_clicked") def previousFeature(self): i = self.listCombo.currentIndex() n = max(0, i-1) self.listCombo.setCurrentIndex(n) self.saveCurrentFeature(n) @pyqtSlot(name="on_nextButton_clicked") def nextFeature(self): self.warned_attr_values = [] # Reset attr warnings i = self.listCombo.currentIndex() c = self.listCombo.count() n = min(i+1, c-1) self.listCombo.setCurrentIndex(n) self.saveCurrentFeature(n) @pyqtSlot(int, name="on_listCombo_activated") def saveCurrentFeature(self, i): if self.settings.value("saveSelectionInProject"): self.layer.setCustomProperty("teamqgisCurrentItem", i) @pyqtSlot(int, name="on_fieldOneNameComboBox_activated") def fieldOneNameComboBox_activated(self, i): self.nameComboBox_activated(i, self.fieldOneNameComboBox) @pyqtSlot(int, name="on_fieldTwoNameComboBox_activated") def fieldTwoNameComboBox_activated(self, i): self.nameComboBox_activated(i, self.fieldTwoNameComboBox) @pyqtSlot(int, name="on_fieldThreeNameComboBox_activated") def fieldThreeNameComboBox_activated(self, i): self.nameComboBox_activated(i, self.fieldThreeNameComboBox) @pyqtSlot(int, name="on_listCombo_currentIndexChanged") def on_listCombo_currentIndexChanged(self, i): feature = self.getCurrentItem() if feature is None: return self.rubber.reset() if self.listCombo.count() > 1: self.setRubber(feature) self.updateValueComboBoxes() # scale to feature self.panScaleToItem(feature) # Update browser self.currentPosLabel.setText("%u/%u" % (i+1, len(self.subset))) # emit signal self.layer.emit(SIGNAL("browserCurrentItem(long)"), feature.id()) @pyqtSlot(int, name="on_panCheck_stateChanged") def on_panCheck_stateChanged(self, i): if self.panCheck.isChecked(): self.scaleCheck.setEnabled(True) feature = self.getCurrentItem() if feature is None: return self.panScaleToItem(feature) else: self.scaleCheck.setEnabled(False) @pyqtSlot(int, name="on_scaleCheck_stateChanged") def on_scaleCheck_stateChanged(self, i): if self.scaleCheck.isChecked(): feature = self.getCurrentItem() if feature is None: return self.panScaleToItem(feature) @pyqtSlot(name="on_editFormButton_clicked") def openFeatureForm(self): if (self.layer.isEditable()): self.layer.commitChanges() else: self.layer.startEditing() @pyqtSlot(name="on_translateRightButton_clicked") def doTranslateRight(self): self.doTranslate((1, 0)) @pyqtSlot(name="on_translateLeftButton_clicked") def doTranslateLeft(self): self.doTranslate((-1, 0)) @pyqtSlot(name="on_translateUpButton_clicked") def doTranslateUp(self): self.doTranslate((0, 1)) @pyqtSlot(name="on_translateDownButton_clicked") def doTranslateDown(self): self.doTranslate((0, -1)) @pyqtSlot(name="on_applyChangesButton_clicked") def applyChanges(self): self.layer.commitChanges() self.layer.startEditing() self.layer.updateExtents() self.iface.mapCanvas().refresh() @pyqtSlot(int, name="on_fieldOneValueComboBox_activated") def on_fieldOneValueComboBox_activated(self, i): self.changeAttribute(i, self.fieldOneNameComboBox, self.fieldOneValueComboBox) @pyqtSlot(int, name="on_fieldTwoValueComboBox_activated") def on_fieldTwoValueComboBox_activated(self, i): self.changeAttribute(i, self.fieldTwoNameComboBox, self.fieldTwoValueComboBox) @pyqtSlot(int, name="on_fieldThreeValueComboBox_activated") def on_fieldThreeValueComboBox_activated(self, i): self.changeAttribute(i, self.fieldThreeNameComboBox, self.fieldThreeValueComboBox)
class VideoWidget(QVideoWidget): def __init__(self, parent=None): """ Constructor """ super().__init__(parent) self.surface = VideoWidgetSurface(self) self.setAttribute(Qt.WA_OpaquePaintEvent) self.Tracking_Video_RubberBand = QRubberBand(QRubberBand.Rectangle, self) self.Censure_RubberBand = QRubberBand(QRubberBand.Rectangle, self) color_blue = QColor(Qt.blue) color_black = QColor(Qt.black) color_amber = QColor(252, 215, 108) pal_blue = QPalette() pal_blue.setBrush(QPalette.Highlight, QBrush(color_blue)) self.Tracking_Video_RubberBand.setPalette(pal_blue) pal_black = QPalette() pal_black.setBrush(QPalette.Highlight, QBrush(color_black)) self.Censure_RubberBand.setPalette(pal_black) self._interaction = InteractionState() self._filterSatate = FilterState() self._isinit = False self._MGRS = False self.drawCesure = [] ( self.poly_coordinates, self.drawPtPos, self.drawLines, self.drawMeasureDistance, self.drawMeasureArea, self.drawPolygon, ) = ([], [], [], [], [], []) # Draw Polygon Canvas Rubberband self.poly_Canvas_RubberBand = QgsRubberBand(iface.mapCanvas(), True) # Polygon type # set rubber band style self.poly_Canvas_RubberBand.setColor(color_amber) self.poly_Canvas_RubberBand.setWidth(3) # Tracking Canvas Rubberband self.Track_Canvas_RubberBand = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.LineGeometry) # set rubber band style self.Track_Canvas_RubberBand.setColor(color_blue) self.Track_Canvas_RubberBand.setWidth(5) # Cursor Canvas Rubberband self.Cursor_Canvas_RubberBand = QgsRubberBand( iface.mapCanvas(), QgsWkbTypes.PointGeometry) self.Cursor_Canvas_RubberBand.setWidth(4) self.Cursor_Canvas_RubberBand.setColor(QColor(255, 100, 100, 250)) self.Cursor_Canvas_RubberBand.setIcon(QgsRubberBand.ICON_FULL_DIAMOND) self.parent = parent.parent() palette = self.palette() palette.setColor(QPalette.Background, Qt.transparent) self.setPalette(palette) self.origin, self.dragPos = QPoint(), QPoint() self.tapTimer = QBasicTimer() self.brush = QBrush(color_black) self.blue_Pen = QPen(color_blue, 3) self.lastMouseX = -1 self.lastMouseY = -1 def removeLastLine(self): """ Remove Last Line Objects """ if self.drawLines: try: if self.drawLines[-1][3] == "mouseMoveEvent": del self.drawLines[-1] # Remove mouseMoveEvent element except Exception: None for pt in range(len(self.drawLines) - 1, -1, -1): del self.drawLines[pt] try: if self.drawLines[pt - 1][0] is None: break except Exception: None self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeLastSegmentLine(self): """ Remove Last Segment Line Objects """ try: if self.drawLines[-1][3] == "mouseMoveEvent": del self.drawLines[-1] # Remove mouseMoveEvent element except Exception: None if self.drawLines: if self.drawLines[-1][0] is None: del self.drawLines[-1] del self.drawLines[-1] self.UpdateSurface() AddDrawLineOnMap(self.drawLines) return def removeAllLines(self): """ Resets Line List """ if self.drawLines: self.drawLines = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawLineOnMap() def ResetDrawMeasureDistance(self): """ Resets Measure Distance List """ self.drawMeasureDistance = [] def ResetDrawMeasureArea(self): """ Resets Measure Area List """ self.drawMeasureArea = [] def removeAllCensure(self): """ Remove All Censure Objects """ if self.drawCesure: self.drawCesure = [] self.UpdateSurface() def removeLastCensured(self): """ Remove Last Censure Objects """ if self.drawCesure: del self.drawCesure[-1] self.UpdateSurface() def removeLastPoint(self): """ Remove All Point Drawer Objects """ if self.drawPtPos: del self.drawPtPos[-1] self.UpdateSurface() RemoveLastDrawPointOnMap() return def removeAllPoint(self): """ Remove All Point Drawer Objects """ if self.drawPtPos: self.drawPtPos = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPointOnMap() return def removeAllPolygon(self): """ Remove All Polygon Drawer Objects """ if self.drawPolygon: self.drawPolygon = [] self.UpdateSurface() # Clear all Layer RemoveAllDrawPolygonOnMap() def removeLastPolygon(self): """ Remove Last Polygon Drawer Objects """ if self.drawPolygon: try: if self.drawPolygon[-1][3] == "mouseMoveEvent": del self.drawPolygon[-1] # Remove mouseMoveEvent element except Exception: None for pt in range(len(self.drawPolygon) - 1, -1, -1): del self.drawPolygon[pt] try: if self.drawPolygon[pt - 1][0] is None: break except Exception: None self.UpdateSurface() # remove last index layer RemoveLastDrawPolygonOnMap() def keyPressEvent(self, event): """Exit fullscreen :type event: QKeyEvent :param event: :return: """ if event.key() == Qt.Key_Escape and self.isFullScreen(): self.setFullScreen(False) event.accept() elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt: self.setFullScreen(not self.isFullScreen()) event.accept() else: super().keyPressEvent(event) def mouseDoubleClickEvent(self, event): """ Mouse double click event :type event: QMouseEvent :param event: :return: """ if GetImageHeight() == 0: return if not vut.IsPointOnScreen(event.x(), event.y(), self.surface): return if GetGCPGeoTransform() is not None and self._interaction.lineDrawer: self.drawLines.append([None, None, None]) return if GetGCPGeoTransform( ) is not None and self._interaction.measureDistance: self.drawMeasureDistance.append([None, None, None]) self.parent.actionMeasureDistance.toggle() return if GetGCPGeoTransform() is not None and self._interaction.measureArea: self.drawMeasureArea.append([None, None, None]) self.parent.actionMeasureArea.toggle() return if GetGCPGeoTransform( ) is not None and self._interaction.polygonDrawer: ok = AddDrawPolygonOnMap(self.poly_coordinates) # Prevent invalid geometry (Polygon with 2 points) if not ok: return self.drawPolygon.append([None, None, None]) # Empty RubberBand for _ in range(self.poly_Canvas_RubberBand.numberOfVertices()): self.poly_Canvas_RubberBand.removeLastPoint() # Empty List self.poly_coordinates = [] return self.UpdateSurface() scr = QApplication.desktop().screenNumber(self) self.setGeometry(QApplication.desktop().screenGeometry(scr)) self.setFullScreen(not self.isFullScreen()) event.accept() def videoSurface(self): """ Return video Surface """ return self.surface def UpdateSurface(self): """ Update Video Surface only is is stopped or paused """ if self.parent.playerState in ( QMediaPlayer.StoppedState, QMediaPlayer.PausedState, ): self.update() QApplication.processEvents() def sizeHint(self): """ This property holds the recommended size for the widget """ return self.surface.surfaceFormat().sizeHint() def currentFrame(self): """ Return current frame QImage """ return self.surface.image def SetInvertColor(self, value): """Set Invert color filter @type value: bool @param value: @return: """ self._filterSatate.invertColorFilter = value def SetObjectTracking(self, value): """Set Object Tracking @type value: bool @param value: @return: """ self._interaction.objectTracking = value def SetMeasureDistance(self, value): """Set measure Distance @type value: bool @param value: @return: """ self._interaction.measureDistance = value def SetMeasureArea(self, value): """Set measure Area @type value: bool @param value: @return: """ self._interaction.measureArea = value def SetHandDraw(self, value): """Set Hand Draw @type value: bool @param value: @return: """ self._interaction.HandDraw = value def SetCensure(self, value): """Set Censure Video Parts @type value: bool @param value: @return: """ self._interaction.censure = value def SetMGRS(self, value): """Set MGRS Cursor Coordinates @type value: bool @param value: @return: """ self._MGRS = value def SetGray(self, value): """Set gray scale @type value: bool @param value: @return: """ self._filterSatate.grayColorFilter = value def SetMirrorH(self, value): """Set Horizontal Mirror @type value: bool @param value: @return: """ self._filterSatate.MirroredHFilter = value def SetNDVI(self, value): """Set NDVI @type value: bool @param value: @return: """ self._filterSatate.NDVI = value def SetEdgeDetection(self, value): """Set Canny Edge filter @type value: bool @param value: @return: """ self._filterSatate.edgeDetectionFilter = value def SetAutoContrastFilter(self, value): """Set Automatic Contrast filter @type value: bool @param value: @return: """ self._filterSatate.contrastFilter = value def SetMonoFilter(self, value): """Set mono filter @type value: bool @param value: @return: """ self._filterSatate.monoFilter = value def RestoreFilters(self): """ Remove and restore all video filters """ self._filterSatate.clear() def RestoreDrawer(self): """ Remove and restore all Drawer Options """ self._interaction.clear() # Magnifier Glass self.dragPos = QPoint() self.tapTimer.stop() def RemoveCanvasRubberbands(self): """ Remove Canvas Rubberbands """ self.poly_Canvas_RubberBand.reset() self.Track_Canvas_RubberBand.reset(QgsWkbTypes.LineGeometry) self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry) def RemoveVideoDrawings(self): """ Remove Video Drawings """ ( self.poly_coordinates, self.drawPtPos, self.drawLines, self.drawMeasureDistance, self.drawMeasureArea, self.drawPolygon, ) = ([], [], [], [], [], []) def paintEvent(self, event): """ @type event: QPaintEvent @param event: @return: """ if not self.surface.isActive(): return self.painter = QPainter(self) self.painter.setRenderHint(QPainter.HighQualityAntialiasing) region = event.region() self.painter.fillRect(region.boundingRect(), self.brush) # Background painter color try: self.surface.paint(self.painter) SetImageSize(self.currentFrame().width(), self.currentFrame().height()) except Exception: None # Prevent draw on video if not started or finished # if self.parent.player.position() == 0: # self.painter.end() # return # Draw On Video draw.drawOnVideo( self.drawPtPos, self.drawLines, self.drawPolygon, self.drawMeasureDistance, self.drawMeasureArea, self.drawCesure, self.painter, self.surface, GetGCPGeoTransform(), ) # Draw On Video Object tracking Object if self._interaction.objectTracking and self._isinit: frame = convertQImageToMat(self.currentFrame()) offset = self.surface.videoRect() # Update tracker result = resize(frame, (offset.width(), offset.height())) ok, bbox = self.tracker.update(result) # Draw bounding box if ok: # check negative values x = bbox[0] + offset.x() y = bbox[1] + offset.y() if vut.IsPointOnScreen(x, y, self.surface): self.painter.setPen(self.blue_Pen) self.painter.setBrush(Qt.transparent) self.painter.drawRect(x, y, bbox[2], bbox[3]) # Get Track object center xc = x + (bbox[2] / 2) yc = y + (bbox[3] / 2) p = QPoint(xc, yc) Longitude, Latitude, _ = vut.GetPointCommonCoords( p, self.surface) # Draw Rubber Band on canvas self.Track_Canvas_RubberBand.addPoint( QgsPointXY(Longitude, Latitude)) else: self._isinit = False del self.tracker # Magnifier Glass if self._interaction.magnifier and not self.dragPos.isNull(): draw.drawMagnifierOnVideo(self, self.dragPos, self.currentFrame(), self.painter) # Stamp On Video if self._interaction.stamp: draw.drawStampOnVideo(self, self.painter) self.painter.end() return def resizeEvent(self, _): """ @type _: QMouseEvent @param _: @return: """ self.surface.updateVideoRect() self.update() # Magnifier Glass if self._interaction.magnifier and not self.dragPos.isNull(): draw.drawMagnifierOnVideo(self, self.dragPos, self.currentFrame(), self.painter) # QApplication.processEvents() def AddMoveEventValue(self, values, Longitude, Latitude, Altitude): """ Remove and Add move value for fluid drawing @type values: list @param values: Points list @type Longitude: float @param Longitude: Longitude value @type Latitude: float @param Latitude: Latitude value @type Altitude: float @param Altitude: Altitude value """ for idx, pt in enumerate(values): if pt[-1] == "mouseMoveEvent": del values[idx] values.append([Longitude, Latitude, Altitude, "mouseMoveEvent"]) self.UpdateSurface() def mouseMoveEvent(self, event, useLast=False): """ @type event: QMouseEvent @param event: @return: """ if event is not None: self.lastMouseX = event.x() self.lastMouseY = event.y() if useLast is False and self.lastMouseX != -1 and self.lastMouseY != -1: # generates an event that simulates a mouse move, because even if # mouse is still, video is running and mouse lat/lon must be # updated. event = QMouseEvent( QEvent.MouseMove, QPoint(self.lastMouseX, self.lastMouseY), Qt.NoButton, Qt.NoButton, Qt.NoModifier, ) else: return # Magnifier can move on black screen for show image borders if self._interaction.magnifier: self.dragPos = event.pos() self.UpdateSurface() # check if the point is on picture (not in black borders) if not vut.IsPointOnScreen(event.x(), event.y(), self.surface): self.setCursor(QCursor(Qt.ArrowCursor)) self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry) return # Prevent draw on video if not started or finished # if self.parent.player.position() == 0: # return # Mouse cursor drawing if (self._interaction.pointDrawer or self._interaction.polygonDrawer or self._interaction.lineDrawer or self._interaction.measureDistance or self._interaction.measureArea or self._interaction.censure or self._interaction.objectTracking): self.setCursor(QCursor(Qt.CrossCursor)) # Cursor Coordinates if GetGCPGeoTransform() is not None: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) tr = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), iface.mapCanvas().mapSettings().destinationCrs(), QgsProject.instance().transformContext(), ) mapPt = tr.transform(QgsPointXY(Longitude, Latitude)) vertices = self.Cursor_Canvas_RubberBand.numberOfVertices() if vertices > 0: self.Cursor_Canvas_RubberBand.removePoint(0, True, 0) self.Cursor_Canvas_RubberBand.movePoint(mapPt, 0) else: self.Cursor_Canvas_RubberBand.addPoint(mapPt) if self._MGRS: try: mgrsCoords = QgsMgrs.toMgrs(Latitude, Longitude) except Exception: mgrsCoords = "" txt = ("<span style='font-size:9pt; font-weight:normal;'>" + ("%s" % mgrsCoords) + "</span>") else: txt = "<span style='font-size:10pt; font-weight:bold;'>Lon : </span>" txt += ("<span style='font-size:9pt; font-weight:normal;'>" + ("%.5f" % Longitude) + "</span>") txt += "<span style='font-size:10pt; font-weight:bold;'> Lat : </span>" txt += ("<span style='font-size:9pt; font-weight:normal;'>" + ("%.5f" % Latitude) + "</span>") if hasElevationModel(): txt += ( "<span style='font-size:10pt; font-weight:bold;'> Alt : </span>" ) txt += ( "<span style='font-size:9pt; font-weight:normal;'>" + ("%.0f" % Altitude) + "</span>") else: txt += ( "<span style='font-size:10pt; font-weight:bold;'> Alt : </span>" ) txt += "<span style='font-size:9pt; font-weight:normal;'>-</span>" self.parent.lb_cursor_coord.setText(txt) # Polygon drawer mouseMoveEvent if self._interaction.polygonDrawer: self.AddMoveEventValue(self.drawPolygon, Longitude, Latitude, Altitude) # Line drawer mouseMoveEvent if self._interaction.lineDrawer: self.AddMoveEventValue(self.drawLines, Longitude, Latitude, Altitude) # Measure Distance drawer mouseMoveEvent if self._interaction.measureDistance and self.drawMeasureDistance: self.AddMoveEventValue(self.drawMeasureDistance, Longitude, Latitude, Altitude) # Measure Area drawer mouseMoveEvent if self._interaction.measureArea and self.drawMeasureArea: self.AddMoveEventValue(self.drawMeasureArea, Longitude, Latitude, Altitude) else: self.parent.lb_cursor_coord.setText( "<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>" + "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" + "<span style='font-size:9pt; font-weight:normal;'>-</span>") # Object tracking rubberband if not self.Tracking_Video_RubberBand.isHidden(): self.Tracking_Video_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) # Censure rubberband if not self.Censure_RubberBand.isHidden(): self.Censure_RubberBand.setGeometry( QRect(self.origin, event.pos()).normalized()) def timerEvent(self, _): """ Time Event (Magnifier method)""" if not self._interaction.magnifier: self.activateMagnifier() def mousePressEvent(self, event): """ @type event: QMouseEvent @param event: @return: """ if GetImageHeight() == 0: return # Prevent draw on video if not started or finished # if self.parent.player.position() == 0: # return if event.button() == Qt.LeftButton: # Magnifier Glass if self._interaction.magnifier: self.dragPos = event.pos() self.tapTimer.stop() self.tapTimer.start(10, self) if not vut.IsPointOnScreen(event.x(), event.y(), self.surface): return # point drawer if GetGCPGeoTransform( ) is not None and self._interaction.pointDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) pointIndex = len(self.drawPtPos) + 1 AddDrawPointOnMap(pointIndex, Longitude, Latitude, Altitude) self.drawPtPos.append([Longitude, Latitude, Altitude]) # polygon drawer if GetGCPGeoTransform( ) is not None and self._interaction.polygonDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.poly_Canvas_RubberBand.addPoint( QgsPointXY(Longitude, Latitude)) self.poly_coordinates.extend(QgsPointXY(Longitude, Latitude)) self.drawPolygon.append([Longitude, Latitude, Altitude]) # line drawer if GetGCPGeoTransform( ) is not None and self._interaction.lineDrawer: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawLines.append([Longitude, Latitude, Altitude]) AddDrawLineOnMap(self.drawLines) self.origin = event.pos() # Object Tracking Interaction if self._interaction.objectTracking: self.Tracking_Video_RubberBand.setGeometry( QRect(self.origin, QSize())) self.Tracking_Video_RubberBand.show() # Censure Interaction if self._interaction.censure: self.Censure_RubberBand.setGeometry(QRect( self.origin, QSize())) self.Censure_RubberBand.show() # Measure Distance drawer if GetGCPGeoTransform( ) is not None and self._interaction.measureDistance: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawMeasureDistance.append( [Longitude, Latitude, Altitude]) # Measure Distance drawer if GetGCPGeoTransform( ) is not None and self._interaction.measureArea: Longitude, Latitude, Altitude = vut.GetPointCommonCoords( event, self.surface) self.drawMeasureArea.append([Longitude, Latitude, Altitude]) # if not called, the paint event is not triggered. self.UpdateSurface() def activateMagnifier(self): """ Activate Magnifier Glass """ self.tapTimer.stop() self.UpdateSurface() def SetMagnifier(self, value): """Set Magnifier Glass @type value: bool @param value: """ self._interaction.magnifier = value # We avoid that the second time we activate the tool, save the previous position. # Always keep the same behavior of the tool if not value: self.dragPos = QPoint() self.tapTimer.stop() def SetStamp(self, value): """Set Stamp @type value: bool @param value: """ self._interaction.stamp = value def SetPointDrawer(self, value): """Set Point Drawer @type value: bool @param value: """ self._interaction.pointDrawer = value def SetLineDrawer(self, value): """Set Line Drawer @type value: bool @param value: """ self._interaction.lineDrawer = value def SetPolygonDrawer(self, value): """Set Polygon Drawer @type value: bool @param value: """ self._interaction.polygonDrawer = value def mouseReleaseEvent(self, _): """ @type event: QMouseEvent @param event: @return: """ # Prevent draw on video if not started or finished # if self.parent.player.position() == 0: # return # Censure Draw Interaction if self._interaction.censure: geom = self.Censure_RubberBand.geometry() self.Censure_RubberBand.hide() self.drawCesure.append([geom]) # Object Tracking Interaction if self._interaction.objectTracking: geom = self.Tracking_Video_RubberBand.geometry() offset = self.surface.videoRect() bbox = ( geom.x() - offset.x(), geom.y() - offset.y(), geom.width(), geom.height(), ) img = self.currentFrame() frame = convertQImageToMat(img) # Remo rubberband on canvas and video self.Tracking_Video_RubberBand.hide() self.Track_Canvas_RubberBand.reset() self.tracker = TrackerMOSSE_create() result = resize(frame, (offset.width(), offset.height())) try: ok = self.tracker.init(result, bbox) except Exception: return if ok: self._isinit = True # Get Traker center xc = bbox[0] + (geom.width() / 2) yc = bbox[1] + (geom.height() / 2) p = QPoint(xc, yc) Longitude, Latitude, _ = vut.GetPointCommonCoords( p, self.surface) # Draw Rubber Band on canvas self.Track_Canvas_RubberBand.addPoint( QgsPointXY(Longitude, Latitude)) else: self._isinit = False def leaveEvent(self, _): """ @type _: QEvent @param _: @return: """ # Remove coordinates label value self.parent.lb_cursor_coord.setText("") # Change cursor self.setCursor(QCursor(Qt.ArrowCursor)) # Reset mouse rubberband self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry)
class RectangleMapTool(QgsMapToolEmitPoint): def __init__(self, iface, dbm, imageRegistry, apisLayer): self.iface = iface self.canvas = self.iface.mapCanvas() self.dbm = dbm self.imageRegistry = imageRegistry self.apisLayer = apisLayer QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setColor(QColor(255, 128, 0, 255)) self.rubberBand.setFillColor(QColor(255, 128, 0, 128)) self.rubberBand.setWidth(1) self.topic = 'image' # 'image', 'site', 'findspot' self.reset() self.imageSelectionListDlg = APISImageSelectionList( self.iface, self.dbm, self.imageRegistry, self.apisLayer, parent=self.iface.mainWindow()) self.siteSelectionListDlg = APISSiteSelectionList( self.iface, self.dbm, self.imageRegistry, self.apisLayer, parent=self.iface.mainWindow()) self.findspotSelectionListDlg = APISFindspotSelectionList( self.iface, self.dbm, self.imageRegistry, self.apisLayer, parent=self.iface.mainWindow()) self.worker = None def setTopic(self, topic): self.topic = topic def deactivate(self): super(RectangleMapTool, self).deactivate() self.deactivated.emit() def activate(self): self.canvas.setCursor( QgsApplication.getThemeCursor(QgsApplication.Cursor.Select)) def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False r = self.rectangle() #QMessageBox.warning(None, "Bild", u"Punkt: {0}".format(epsg)) srcCrs = self.canvas.mapSettings().destinationCrs() destCrs = QgsCoordinateReferenceSystem( 4312, QgsCoordinateReferenceSystem.EpsgCrsId) ct = QgsCoordinateTransform(srcCrs, destCrs, QgsProject.instance()) if r is None: #QMessageBox.warning(None, "Bild", u"Punkt: {0}".format(self.endPoint.wellKnownText())) p = QgsGeometry.fromPointXY(self.endPoint) p.transform(ct) #QMessageBox.warning(None, "Bild", u"Punkt: {0}".format(self.endPoint.x())) #self.openImageSelectionListDialogByLocation(p.asWkt(8)) self.startWorker(p.asWkt(8)) else: #.warning(None, "Bild", u"Rechteck: {0}, {1}, {2}, {3}".format(r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum())) r2 = QgsGeometry.fromRect(r) r2.transform(ct) #QMessageBox.warning(None, "Bild", u"Polygon: {0}".format(r2.asoWkt(8))) #self.openImageSelectionListDialogByLocation(r2.asoWkt(8)) self.startWorker(r2.asWkt(8)) #print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() def openImageSelectionListDialogByLocation(self, query): # progressMessageBar = self.iface.messageBar().createMessage("Luftbilder werden gesucht") # progress = QProgressBar() # progress.setMinimum(0) # progress.setMaximum(0) # progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter) # progressMessageBar.layout().addWidget(progress) # self.iface.messageBar().pushWidget(progressMessageBar, self.iface.messageBar().INFO) res = self.imageSelectionListDlg.loadImageListBySqlQuery(query) if res: self.imageSelectionListDlg.show() if self.imageSelectionListDlg.exec_(): pass self.rubberBand.hide() def openSiteSelectionListDialogByLocation(self, query): info = u"gefunden für den ausgewählten Bereich." res = self.siteSelectionListDlg.loadSiteListBySpatialQuery(query, info) if res: self.siteSelectionListDlg.show() if self.siteSelectionListDlg.exec_(): pass self.rubberBand.hide() def openFindspotSelectionListDialogByLocation(self, query): info = u"gefunden für den ausgewählten Bereich." res = self.findspotSelectionListDlg.loadFindspotListBySpatialQuery( query, info) if res: self.findspotSelectionListDlg.show() #if self.findspotSelectionListDlg.exec_(): # pass self.rubberBand.hide() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def startWorker(self, geometry): # create a new worker instance if self.worker is None: worker = Worker(self.dbm, geometry, self.topic) # configure the QgsMessageBar messageBar = self.iface.messageBar().createMessage( u'Räumliche Suche wird durchgeführt ...', ) progressBar = QProgressBar() progressBar.setMinimum(0) progressBar.setMaximum(0) progressBar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) cancelButton = QPushButton() cancelButton.setText('Cancel') cancelButton.clicked.connect(self.killWorker) messageBar.layout().addWidget(progressBar) self.progressBar = progressBar messageBar.layout().addWidget(cancelButton) self.iface.messageBar().pushWidget(messageBar, Qgis.Info) self.messageBar = messageBar # start the worker in a new thread thread = QThread(self) worker.moveToThread(thread) worker.finished.connect(self.workerFinished) worker.error.connect(self.workerError) #worker.progress.connect(progressBar.setValue) thread.started.connect(worker.run) thread.start() self.thread = thread self.worker = worker def killWorker(self): self.worker.kill() self.progressBar.setMaximum(100) self.progressBar.setValue(100) def workerFinished(self, query, topic): # clean up the worker and thread self.worker.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() # remove widget from message bar self.iface.messageBar().popWidget(self.messageBar) if query is not None: # report the result #query = result if not self.worker.killed: if topic == 'image': self.openImageSelectionListDialogByLocation(query) elif topic == 'site': self.openSiteSelectionListDialogByLocation(query) elif topic == 'findspot': self.openFindspotSelectionListDialogByLocation(query) else: self.rubberBand.hide() #self.iface.messageBar().pushMessage('Result') else: # notify the user that something went wrong self.iface.messageBar().pushMessage( 'Something went wrong! See the message log for more information.', level=Qgis.Critical, duration=3) self.worker = None def workerError(self, e, exception_string): QgsMessageLog.logMessage( 'APIS Search Worker thread raised an exception:\n'.format( exception_string), tag='APIS', level=Qgis.Critical)
class LeggerMapVisualisation(object): # self.line_layer.crs() def __init__(self, iface, source_crs): self.iface = iface self.source_crs = source_crs # temp layer for side profile trac self.rb = QgsRubberBand(self.iface.mapCanvas()) self.rb.setColor(Qt.red) self.rb.setWidth(2) # temp layer for last selected point self.point_markers = [] self.active_route = None self.hover_marker = QgsVertexMarker(self.iface.mapCanvas()) self.hover_marker.setIconType(QgsVertexMarker.ICON_X) self.hover_marker.setColor(Qt.red) self.hover_marker.setPenWidth(6) self.dist_calc = QgsDistanceArea() def close(self): self.reset() self.iface.mapCanvas().scene().removeItem(self.hover_marker) def set_sideview_route(self, route): self.reset() self.active_route = route transform = QgsCoordinateTransform( self.source_crs, self.iface.mapCanvas().mapRenderer().destinationCrs()) for pnt in route.path_vertexes: t_pnt = transform.transform(pnt) self.rb.addPoint(t_pnt) for point, point_id, dist in route.path_points: marker = QgsVertexMarker(self.iface.mapCanvas()) marker.setIconType(QgsVertexMarker.ICON_CIRCLE) marker.setColor(Qt.red) marker.setPenWidth(4) marker.setCenter(transform.transform(point)) self.point_markers.append(marker) def reset(self): self.rb.reset() self.active_route = None for marker in self.point_markers: self.iface.mapCanvas().scene().removeItem(marker) self.point_markers = [] self.hover_marker.setCenter(QgsPoint(0.0, 0.0)) def hover_graph(self, meters_from_start): transform = QgsCoordinateTransform( self.source_crs, self.iface.mapCanvas().mapRenderer().destinationCrs()) if self.active_route is None: return if meters_from_start < 0.0: meters_from_start = 0.0 elif (len(self.active_route.path) > 0 and meters_from_start > self.active_route.path[-1][-1][1]): meters_from_start = self.active_route.path[-1][-1][1] for route_part in self.active_route.path: if meters_from_start <= route_part[-1][1]: for part in route_part: if meters_from_start <= part[1]: if part[3] == 1: distance_on_line = meters_from_start - part[0] else: distance_on_line = part[1] - meters_from_start length, unit_type = self.dist_calc.convertMeasurement( distance_on_line, QGis.Meters, QGis.Degrees, False) # QGis.Degrees point = part[4].geometry().interpolate(length) self.hover_marker.setCenter( transform.transform(point.asPoint())) return def hover_map(self, point_geometry): pass def show_selectable_points(self, graph_tree): pass def hide_selectable_points(self): pass
class RectangleMapTool(QgsMapToolEmitPoint): rectangleCreated = pyqtSignal() deactivated = pyqtSignal() def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setColor(QColor(255, 0, 0, 100)) self.rubberBand.setWidth(2) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False if self.rectangle() is not None: self.rectangleCreated.emit() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(startPoint.x(), endPoint.y()) point3 = QgsPointXY(endPoint.x(), endPoint.y()) point4 = QgsPointXY(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) # True to update canvas self.rubberBand.addPoint(point4, True) self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or \ self.startPoint.y() == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self.rectangle(): return False if rect is None: self.reset() else: self.startPoint = QgsPointXY(rect.xMaximum(), rect.yMaximum()) self.endPoint = QgsPointXY(rect.xMinimum(), rect.yMinimum()) self.showRect(self.startPoint, self.endPoint) return True def deactivate(self): QgsMapTool.deactivate(self) self.deactivated.emit()
class PickerAOIPointTool(QgsMapTool): def __init__(self, cad): QgsMapTool.__init__(self, cad.render_widget.canvas) self.cad = cad # set rubber band style color = QColor("red") color.setAlpha(70) # create the main polygon rubber band self.rubber_band = QgsRubberBand(cad.render_widget.canvas, QgsWkbTypes.PolygonGeometry) self.rubber_band.setColor(color) self.rubber_band.setWidth(3) # create the mouse/tmp polygon rubber band, this is main rubber band + current mouse position self.tmp_rubber_band = QgsRubberBand(cad.render_widget.canvas, QgsWkbTypes.PolygonGeometry) self.tmp_rubber_band.setColor(color) self.tmp_rubber_band.setWidth(3) self.tmp_rubber_band.setLineStyle(Qt.DotLine) def finish_drawing(self): self.rubber_band = None self.tmp_rubber_band = None # restart point tool self.clean() self.cad.render_widget.canvas.unsetMapTool(self) self.cad.render_widget.canvas.setMapTool( self.cad.render_widget.pan_zoom_tool) def canvasMoveEvent(self, event): if self.tmp_rubber_band is None: return if self.tmp_rubber_band and self.tmp_rubber_band.numberOfVertices(): x = event.pos().x() y = event.pos().y() point = self.cad.render_widget.canvas.getCoordinateTransform( ).toMapCoordinates(x, y) self.tmp_rubber_band.removeLastPoint() self.tmp_rubber_band.addPoint(point) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.rubber_band.removeLastPoint() self.tmp_rubber_band.removeLastPoint() if event.key() == Qt.Key_Escape: self.rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.tmp_rubber_band.reset(QgsWkbTypes.PolygonGeometry) def canvasPressEvent(self, event): if self.rubber_band is None: self.finish_drawing() return # new point on polygon if event.button() == Qt.LeftButton: x = event.pos().x() y = event.pos().y() point = self.cad.render_widget.canvas.getCoordinateTransform( ).toMapCoordinates(x, y) self.rubber_band.addPoint(point) self.tmp_rubber_band.addPoint(point) # save polygon if event.button() == Qt.RightButton: if self.rubber_band and self.rubber_band.numberOfVertices(): if self.rubber_band.numberOfVertices() < 3: self.finish_drawing() return self.tmp_rubber_band.removeLastPoint() new_feature = QgsFeature() new_feature.setGeometry(self.rubber_band.asGeometry()) self.cad.rubber_bands.append(self.rubber_band) self.cad.tmp_rubber_band.append(self.tmp_rubber_band) self.rubber_band = None self.tmp_rubber_band = None self.finish_drawing() # add the new feature and update the statistics self.cad.aoi_changes(new_feature) def keyReleaseEvent(self, event): if event.key() in [ Qt.Key_Up, Qt.Key_Down, Qt.Key_Right, Qt.Key_Left, Qt.Key_PageUp, Qt.Key_PageDown ]: QTimer.singleShot( 10, self.cad.render_widget.parent_view.canvas_changed)
class RectangleMapTool(QgsMapToolEmitPoint): """Map tool that lets the user define the analysis extents.""" rectangle_created = pyqtSignal() deactivated = pyqtSignal() def __init__(self, canvas): """Constructor for the map tool. :param canvas: Canvas that tool will interact with. :type canvas: QgsMapCanvas """ self.canvas = canvas self.start_point = None self.end_point = None self.is_emitting_point = False QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubber_band = QgsRubberBand(self.canvas, geometryType=QGis.Line) self.rubber_band.setColor(QColor(0, 0, 240, 100)) # Needs QGIS 2.6 # self.rubber_band.setFillColor(QColor(0, 0, 240, 0)) self.rubber_band.setWidth(1) self.reset() def reset(self): """ Clear the rubber band for the analysis extents. """ self.start_point = self.end_point = None self.is_emitting_point = False self.rubber_band.reset(QGis.Polygon) def canvasPressEvent(self, e): """ Handle canvas press events so we know when user is capturing the rect. :param e: A Qt event object. :type: QEvent """ self.start_point = self.toMapCoordinates(e.pos()) self.end_point = self.start_point self.is_emitting_point = True self.show_rectangle(self.start_point, self.end_point) def canvasReleaseEvent(self, e): """ Handle canvas release events has finished capturing e :param e: A Qt event object. :type: QEvent """ _ = e self.is_emitting_point = False self.rectangle_created.emit() def canvasMoveEvent(self, e): """ :param e: :return: """ if not self.is_emitting_point: return self.end_point = self.toMapCoordinates(e.pos()) self.show_rectangle(self.start_point, self.end_point) def show_rectangle(self, start_point, end_point): """ Show the rectangle on the canvas. :param start_point: QGIS Point object representing the origin ( top left). :type start_point: QgsPoint :param end_point: QGIS Point object representing the contra-origin ( bottom right). :type end_point: QgsPoint :return: """ self.rubber_band.reset(QGis.Polygon) if (start_point.x() == end_point.x() or start_point.y() == end_point.y()): return point1 = start_point point2 = QgsPoint(end_point.x(), start_point.y()) point3 = end_point point4 = QgsPoint(start_point.x(), end_point.y()) update_canvas = False self.rubber_band.addPoint(point1, update_canvas) self.rubber_band.addPoint(point2, update_canvas) self.rubber_band.addPoint(point3, update_canvas) self.rubber_band.addPoint(point4, update_canvas) # noinspection PyArgumentEqualDefault # no False so canvas will update # close the polygon otherwise it shows as a filled rect self.rubber_band.addPoint(point1) self.rubber_band.show() def rectangle(self): """ Accessor for the rectangle. :return: A rectangle showing the designed extent. :rtype: QgsRectangle """ if self.start_point is None or self.end_point is None: return None elif self.start_point.x() == self.end_point.x() or \ self.start_point.y() == self.end_point.y(): return None return QgsRectangle(self.start_point, self.end_point) def set_rectangle(self, rectangle): """ Set the rectangle for the selection. :param rectangle: :return: """ if rectangle == self.rectangle(): return False if rectangle is None: self.reset() else: self.start_point = QgsPoint(rectangle.xMinimum(), rectangle.yMinimum()) self.end_point = QgsPoint(rectangle.xMaximum(), rectangle.yMaximum()) self.show_rectangle(self.start_point, self.end_point) return True def deactivate(self): """ Disable the tool. """ self.rubber_band.reset(QGis.Polygon) QgsMapTool.deactivate(self) self.deactivated.emit()
class GeodesicMeasureDialog(QDialog, FORM_CLASS): def __init__(self, iface, parent): super(GeodesicMeasureDialog, self).__init__(parent) self.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() qset = QSettings() self.restoreGeometry( qset.value("ShapeTools/MeasureDialogGeometry", QByteArray(), type=QByteArray)) self.closeButton.clicked.connect(self.closeDialog) self.newButton.clicked.connect(self.newDialog) self.saveToLayerButton.clicked.connect(self.saveToLayer) self.saveToLayerButton.setEnabled(False) self.unitsComboBox.addItems(DISTANCE_LABELS) self.tableWidget.setColumnCount(3) self.tableWidget.setSortingEnabled(False) self.tableWidget.setHorizontalHeaderLabels( [tr('Heading To'), tr('Heading From'), tr('Distance')]) self.unitsComboBox.activated.connect(self.unitsChanged) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.lastMotionPt = None self.unitsChanged() self.currentDistance = 0.0 self.pointRb = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointRb.setColor(settings.rubberBandColor) self.pointRb.setIconSize(10) self.lineRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.lineRb.setColor(settings.rubberBandColor) self.lineRb.setWidth(3) self.tempRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRb.setColor(settings.rubberBandColor) self.tempRb.setWidth(3) def ready(self): return self.activeMeasuring def stop(self): self.activeMeasuring = False self.lastMotionPt = None def closeEvent(self, event): self.closeDialog() def closeDialog(self): self.clear() QSettings().setValue("ShapeTools/MeasureDialogGeometry", self.saveGeometry()) self.close() def newDialog(self): self.clear() self.initGeodLabel() def initGeodLabel(self): label = tr('Ellipsoid: ') + settings.ellipseDescription self.geodLabel.setText(label) def keyPressed(self, key): index = len(self.capturedPoints) if index <= 0: return if self.motionReady(): if self.lastMotionPt == None: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.lastMotionPt) else: if index < 2: return (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 2], self.capturedPoints[index - 1]) distance = self.unitDistance(distance) clipboard = QApplication.clipboard() if key == Qt.Key_1 or key == Qt.Key_F: s = '{:.{prec}f}'.format(startAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading to {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_2 or key == Qt.Key_T: s = '{:.{prec}f}'.format(endAngle, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Heading from {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_3 or key == Qt.Key_D: s = '{:.{prec}f}'.format(distance, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) elif key == Qt.Key_4 or key == Qt.Key_A: total = 0.0 num = len(self.capturedPoints) for i in range(1, num): (d, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += d total = self.unitDistance(total) # Add in the motion distance if self.motionReady(): total += distance s = '{:.{prec}f}'.format(total, prec=settings.measureSignificantDigits) clipboard.setText(s) self.iface.messageBar().pushMessage( "", "Total distance {} copied to the clipboard".format(s), level=Qgis.Info, duration=3) else: return def unitsChanged(self): label = "Distance [{}]".format( DISTANCE_LABELS[self.unitsComboBox.currentIndex()]) item = QTableWidgetItem(label) self.tableWidget.setHorizontalHeaderItem(2, item) ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: item = QTableWidgetItem('{:.4f}'.format( self.unitDistance(self.distances[i]))) self.tableWidget.setItem(i, 2, item) i += 1 self.formatTotal() def motionReady(self): if len(self.capturedPoints) > 0 and self.activeMeasuring: return True return False def addPoint(self, pt, button): self.currentDistance = 0 index = len(self.capturedPoints) if index > 0 and pt == self.capturedPoints[index - 1]: # the clicked point is the same as the previous so just ignore it return self.capturedPoints.append(pt) # Add rubber band points canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) ptCanvas = transform.transform(pt.x(), pt.y()) self.pointRb.addPoint(ptCanvas, True) # If there is more than 1 point add it to the table if index > 0: self.saveToLayerButton.setEnabled(True) (distance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], self.capturedPoints[index]) self.distances.append(distance) self.insertParams(index, distance, startAngle, endAngle) # Add Rubber Band Line linePts = self.getLinePts(distance, self.capturedPoints[index - 1], self.capturedPoints[index]) self.lineRb.addGeometry(QgsGeometry.fromPolylineXY(linePts), None) self.formatTotal() def inMotion(self, pt): index = len(self.capturedPoints) if index <= 0: return (self.currentDistance, startAngle, endAngle) = self.calcParameters(self.capturedPoints[index - 1], pt) self.insertParams(index, self.currentDistance, startAngle, endAngle) self.formatTotal() linePts = self.getLinePts(self.currentDistance, self.capturedPoints[index - 1], pt) self.lastMotionPt = pt self.tempRb.setToGeometry(QgsGeometry.fromPolylineXY(linePts), None) def calcParameters(self, pt1, pt2): l = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x()) az2 = (l['azi2'] + 180) % 360.0 if az2 > 180: az2 = az2 - 360.0 az1 = l['azi1'] # Check to see if the azimuth values should be in the range or 0 to 360 # The default is -180 to 180 if settings.mtAzMode: if az1 < 0: az1 += 360.0 if az2 < 0: az2 += 360 return (l['s12'], az1, az2) def getLinePts(self, distance, pt1, pt2): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(epsg4326, canvasCrs, QgsProject.instance()) pt1c = transform.transform(pt1.x(), pt1.y()) pt2c = transform.transform(pt2.x(), pt2.y()) if distance < 10000: return [pt1c, pt2c] l = geod.InverseLine(pt1.y(), pt1.x(), pt2.y(), pt2.x()) n = int(math.ceil(distance / 10000.0)) if n > 20: n = 20 seglen = distance / n pts = [pt1c] for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) ptc = transform.transform(g['lon2'], g['lat2']) pts.append(ptc) pts.append(pt2c) return pts def saveToLayer(self): units = self.unitDesignator() canvasCrs = self.canvas.mapSettings().destinationCrs() fields = QgsFields() fields.append(QgsField("label", QVariant.String)) fields.append(QgsField("value", QVariant.Double)) fields.append(QgsField("units", QVariant.String)) fields.append(QgsField("heading_to", QVariant.Double)) fields.append(QgsField("heading_from", QVariant.Double)) fields.append(QgsField("total_dist", QVariant.Double)) layer = QgsVectorLayer("LineString?crs={}".format(canvasCrs.authid()), "Measurements", "memory") dp = layer.dataProvider() dp.addAttributes(fields) layer.updateFields() num = len(self.capturedPoints) total = 0.0 for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) total += distance total = self.unitDistance(total) for i in range(1, num): (distance, startA, endA) = self.calcParameters(self.capturedPoints[i - 1], self.capturedPoints[i]) pts = self.getLinePts(distance, self.capturedPoints[i - 1], self.capturedPoints[i]) distance = self.unitDistance(distance) feat = QgsFeature(layer.fields()) feat.setAttribute(0, "{:.2f} {}".format(distance, units)) feat.setAttribute(1, distance) feat.setAttribute(2, units) feat.setAttribute(3, startA) feat.setAttribute(4, endA) feat.setAttribute(5, total) feat.setGeometry(QgsGeometry.fromPolylineXY(pts)) dp.addFeatures([feat]) label = QgsPalLayerSettings() label.fieldName = 'label' try: label.placement = QgsPalLayerSettings.Line except: label.placement = QgsPalLayerSettings.AboveLine format = label.format() format.setColor(settings.measureTextColor) format.setNamedStyle('Bold') label.setFormat(format) labeling = QgsVectorLayerSimpleLabeling(label) layer.setLabeling(labeling) layer.setLabelsEnabled(True) renderer = layer.renderer() renderer.symbol().setColor(settings.measureLineColor) renderer.symbol().setWidth(0.5) layer.updateExtents() QgsProject.instance().addMapLayer(layer) def insertParams(self, position, distance, startAngle, endAngle): if position > self.tableWidget.rowCount(): self.tableWidget.insertRow(position - 1) item = QTableWidgetItem('{:.4f}'.format(self.unitDistance(distance))) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 2, item) item = QTableWidgetItem('{:.4f}'.format(startAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 0, item) item = QTableWidgetItem('{:.4f}'.format(endAngle)) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.tableWidget.setItem(position - 1, 1, item) def formatTotal(self): total = self.currentDistance ptcnt = len(self.capturedPoints) if ptcnt >= 2: i = 0 while i < ptcnt - 1: total += self.distances[i] i += 1 self.distanceLineEdit.setText('{:.2f}'.format( self.unitDistance(total))) def updateRBColor(self): self.pointRb.setColor(settings.rubberBandColor) self.lineRb.setColor(settings.rubberBandColor) self.tempRb.setColor(settings.rubberBandColor) def clear(self): self.tableWidget.setRowCount(0) self.capturedPoints = [] self.distances = [] self.activeMeasuring = True self.currentDistance = 0.0 self.distanceLineEdit.setText('') self.pointRb.reset(QgsWkbTypes.PointGeometry) self.lineRb.reset(QgsWkbTypes.LineGeometry) self.tempRb.reset(QgsWkbTypes.LineGeometry) self.saveToLayerButton.setEnabled(False) self.updateRBColor() def unitDistance(self, distance): units = self.unitsComboBox.currentIndex() if units == 0: # kilometers return distance / 1000.0 elif units == 1: # meters return distance elif units == 2: # centimeters return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceCentimeters) elif units == 3: # miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceMiles) elif units == 4: # yards return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceYards) elif units == 5: # feet return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) elif units == 6: # inches return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceFeet) * 12 elif units == 7: # nautical miles return distance * QgsUnitTypes.fromUnitToUnitFactor( QgsUnitTypes.DistanceMeters, QgsUnitTypes.DistanceNauticalMiles) def unitDesignator(self): units = self.unitsComboBox.currentIndex() return unitsAbbr[units]
class AuditDialog(QDialog, Ui_audit, SettingDialog): rejectShowEvent = pyqtSignal() performSearchAtShowEvent = pyqtSignal() def __init__(self, iface, layerId=None, featureId=None): QDialog.__init__(self) self.setupUi(self) self.settings = MySettings() SettingDialog.__init__(self, self.settings, False, True) # column chooser, advanced search options #init variables self.layerId = layerId self.featureId = featureId self.layer = None self.rubber = QgsRubberBand(iface.mapCanvas()) self.mapCanvas = iface.mapCanvas() self.resuts = dict() # connect "pan and show geometry" check box to draw in rubber band self.panShowGeometry.clicked.connect(self.displayGeomDifference) # reject properly showEvent if checking fails self.rejectShowEvent.connect(self.reject, Qt.QueuedConnection) # start search directly at the end of showEvent if enough params self.performSearchAtShowEvent.connect(self.on_searchButton_clicked, Qt.QueuedConnection) # setup layer - field combo, with primary key selector as field self.layerComboManager = VectorLayerCombo(self.layerCombo, layerId, {"dataProvider": "postgres", "finishInit": False, "skipLayers": [lambda: self.settings.value("logLayer")]}) self.layerComboManager.finishInit() # log layer self.logLayer = LogLayer() self.logLayer.setProgressMax.connect(self.progressBar.setMaximum) self.logLayer.setProgressMin.connect(self.progressBar.setMinimum) self.logLayer.setProgressValue.connect(self.progressBar.setValue) # logged actions table self.loggedActionsLayout = QGridLayout(self.loggedActionsWidget) self.loggedActionsTable = LoggedActionsTable(self.loggedActionsWidget) self.loggedActionsLayout.addWidget(self.loggedActionsTable, 0, 0, 1, 1) self.loggedActionsTable.itemSelectionChanged.connect(self.displayDifference) # difference viewer self.differenceLayout = QGridLayout(self.differenceViewerWidget) self.differenceViewer = DifferenceViewer(self.differenceViewerWidget) self.differenceLayout.addWidget(self.differenceViewer, 0, 0, 1, 1) # set dates now = QDateTime.currentDateTime() self.searchBeforeDate.setDateTime(now) self.searchAfterDate.setDateTime(now.addDays(-7)) # finish ui self.buttonDisplayMode(False) self.restoreButton.setEnabled(False) if featureId is not None: self.featureEdit.setText("%s" % featureId) self.adjustSize() def closeEvent(self, e): self.rubber.reset() def showEvent(self, e): SettingDialog.showEvent(self, e) while not self.logLayer.isValid(): if not LoggedActionsTableChooserDialog().exec_(): self.rejectShowEvent.emit() return if self.layerId is not None: self.layerCombo.setEnabled(False) layer = self.layerComboManager.getLayer() if layer is None: self.rejectShowEvent.emit() return if self.featureId is not None: self.featureEdit.setEnabled(False) f = QgsFeature() featReq = QgsFeatureRequest().setFilterFid(self.featureId).setFlags(QgsFeatureRequest.NoGeometry) if layer.getFeatures(featReq).nextFeature(f) is False: self.rejectShowEvent.emit() return self.performSearchAtShowEvent.emit() else: layer = self.mapCanvas.currentLayer() self.layerComboManager.setLayer(layer) @pyqtSignature("on_layerCombo_currentIndexChanged(int)") def on_layerCombo_currentIndexChanged(self, i): self.layer = self.layerComboManager.getLayer() self.panShowGeometry.setEnabled(self.layer is not None and self.layer.hasGeometryType()) @pyqtSignature("on_stopButton_clicked()") def on_stopButton_clicked(self): self.logLayer.interrupt() @pyqtSignature("on_searchButton_clicked()") def on_searchButton_clicked(self): self.layer = self.layerComboManager.getLayer() pkeyName = primaryKey(self.layer) if self.layer is None or pkeyName is None: return self.loggedActionsTable.geomColumn = self.layer.hasGeometryType() featureId = int(self.featureEdit.text() or 0) searchBeforeDate = QDateTime() if self.searchBefore.isChecked(): searchBeforeDate = self.searchAfterDate.dateTime() searchAfterDate = QDateTime() if self.searchAfter.isChecked(): searchAfterDate = self.searchAfterDate.dateTime() self.buttonDisplayMode(True) self.results = self.logLayer.performSearch(self.layer, featureId, pkeyName, self.searchInserts.isChecked(), self.searchUpdates.isChecked(), self.searchDeletes.isChecked(), self.searchOnlyGeometry.isChecked(), searchAfterDate, searchBeforeDate) self.buttonDisplayMode(False) self.panShowGeometry.setEnabled(self.layer.hasGeometryType()) self.displayLoggedActions() def buttonDisplayMode(self, searchOn): self.searchButton.setVisible(not searchOn) self.stopButton.setVisible(searchOn) self.progressBar.setVisible(searchOn) def displayLoggedActions(self): self.differenceViewer.clearRows() self.loggedActionsTable.displayColumns() self.loggedActionsTable.displayRows(self.results) def displayDifference(self): self.differenceViewer.clearRows() self.restoreButton.setEnabled(False) item = self.loggedActionsTable.selectedItems() if len(item) == 0: return rowId = item[0].data(Qt.UserRole) logRow = self.results[rowId] if logRow.featureLayer.isEditable(): self.restoreButton.setEnabled(True) self.differenceViewer.display(logRow) self.displayGeomDifference() def displayGeomDifference(self): self.rubber.reset() item = self.loggedActionsTable.selectedItems() if len(item) == 0: return rowId = item[0].data(Qt.UserRole) logRow = self.results[rowId] if self.layer.hasGeometryType() and self.panShowGeometry.isChecked(): geom = logRow.geometry() self.rubber.setToGeometry(geom, self.layer) panTo = self.mapCanvas.mapRenderer().layerExtentToOutputExtent(self.layer, geom.boundingBox()) panTo.scale(1.5) self.mapCanvas.setExtent(panTo) self.mapCanvas.refresh() @pyqtSignature("on_columnChooserButton_clicked()") def on_columnChooserButton_clicked(self): ColumnChooserDialog().exec_() self.loggedActionsTable.displayColumns() self.loggedActionsTable.displayRows(self.results) @pyqtSignature("on_restoreButton_clicked()") def on_restoreButton_clicked(self): item = self.loggedActionsTable.selectedItems() if len(item) == 0: return rowId = item[0].data(Qt.UserRole) logRow = self.results[rowId] if not logRow.featureLayer.isEditable(): return logRow.restoreFeature() self.mapCanvas.refresh()
class ObstacleAreaJigSelectArea(QgsMapTool): def __init__(self, canvas, areaType): self.mCanvas = canvas self.areaType = areaType QgsMapTool.__init__(self, canvas) self.mCursor = Qt.ArrowCursor self.mRubberBand = None self.mDragging = False self.mSelectRect = QRect() self.mRubberBandResult = None self.mSnapper = QgsMapCanvasSnapper(canvas) self.lineCount = 0 self.resultGeomList = [] self.geomList = [] self.area = None self.isFinished = False # QgsRubberBand* mRubberBand; # def reset(self): # self.startPoint = None # self.endPoint = None # self.isDrawing = False # SelectByRect.RubberRect.reset(QGis.Polygon) # self.layer = self.canvas.currentLayer() def canvasPressEvent(self, e): QgisHelper.ClearRubberBandInCanvas(define._canvas) self.mSelectRect.setRect(0, 0, 0, 0) self.mRubberBand = QgsRubberBand(self.mCanvas, QGis.Polygon) self.startPoint, self.pointID, self.layer = self.snapPoint(e.pos()) def canvasMoveEvent(self, e): if self.areaType == ProtectionAreaType.Secondary: if self.lineCount == 0: define._messageLabel.setText( "Select a line or arc representing the INNER edge of the secondary area." ) elif self.lineCount == 1: define._messageLabel.setText( "Select a line representing the OUTER edge of the secondary area." ) elif self.areaType == ProtectionAreaType.Primary: define._messageLabel.setText("") elif self.areaType == ProtectionAreaType.PrimaryAndSecondary: if self.lineCount == 0: define._messageLabel.setText( "Select a line or arc representing the INNER edge of the FIRST secondary area." ) elif self.lineCount == 1: define._messageLabel.setText( "Select a line representing the OUTER edge of the FIRST secondary area." ) elif self.lineCount == 2: define._messageLabel.setText( "Select a line or arc representing the INNER edge of the SECOND secondary area." ) elif self.lineCount == 3: define._messageLabel.setText( "Select a line representing the OUTER edge of the SECOND secondary area." ) else: define._messageLabel.setText("") if (e.buttons() != Qt.LeftButton): return if (not self.mDragging): self.mDragging = True self.mSelectRect.setTopLeft(e.pos()) self.mSelectRect.setBottomRight(e.pos()) QgsMapToolSelectUtils.setRubberBand(self.mCanvas, self.mSelectRect, self.mRubberBand) def canvasReleaseEvent(self, e): self.endPoint, self.pointID, self.layer = self.snapPoint(e.pos()) vlayer = QgsMapToolSelectUtils.getCurrentVectorLayer(self.mCanvas) if (vlayer == None): if (self.mRubberBand != None): self.mRubberBand.reset(QGis.Polygon) del self.mRubberBand self.mRubberBand = None self.mDragging = False return if (not self.mDragging): QgsMapToolSelectUtils.expandSelectRectangle( self.mSelectRect, vlayer, e.pos()) else: if (self.mSelectRect.width() == 1): self.mSelectRect.setLeft(self.mSelectRect.left() + 1) if (self.mSelectRect.height() == 1): self.mSelectRect.setBottom(self.mSelectRect.bottom() + 1) if (self.mRubberBand != None): QgsMapToolSelectUtils.setRubberBand(self.mCanvas, self.mSelectRect, self.mRubberBand) selectGeom = self.mRubberBand.asGeometry() selectedFeatures = QgsMapToolSelectUtils.setSelectFeaturesOrRubberband_Tas_1( self.mCanvas, selectGeom, e) if len(selectedFeatures) > 0: self.lineCount += 1 geom = selectedFeatures[0].geometry() resultArray = QgisHelper.findArcOrLineInLineGeometry( geom, selectGeom) # if resultArray != None: # bulge = MathHelper.smethod_60(resultArray[0], resultArray[int(len(resultArray)/2)], resultArray[len(resultArray)-1]) # bulge1 = MathHelper.smethod_60(resultArray[len(resultArray)-1], resultArray[int(len(resultArray)/2)], resultArray[0]) # n = 0 pointArray0 = geom.asPolyline() self.resultGeomList.append(resultArray) self.geomList.append(pointArray0) if self.lineCount == 2 and self.areaType != ProtectionAreaType.PrimaryAndSecondary and self.areaType != ProtectionAreaType.Complex: self.area = self.makeArea(self.resultGeomList, self.areaType) pointArray = self.getPointArray( self.resultGeomList).method_14_closed() self.mRubberBandResult = None self.mRubberBandResult = QgsRubberBand( self.mCanvas, QGis.Polygon) self.mRubberBandResult.setFillColor( QColor(255, 255, 255, 100)) self.mRubberBandResult.setBorderColor(QColor(0, 0, 0)) for point in pointArray: self.mRubberBandResult.addPoint(point) self.mRubberBandResult.show() self.emit(SIGNAL("outputResult"), self.area, self.mRubberBandResult) self.lineCount = 0 self.resultGeomList = [] self.isFinished = True # self.rubberBandLine.reset(QGis.Line) elif self.lineCount == 4 and self.areaType == ProtectionAreaType.PrimaryAndSecondary: self.area = self.makeArea(self.resultGeomList, self.areaType) pointArray = self.getPointArray( [self.resultGeomList[1], self.resultGeomList[3]]).method_14_closed() self.mRubberBandResult = None self.mRubberBandResult = QgsRubberBand( self.mCanvas, QGis.Polygon) self.mRubberBandResult.setFillColor( QColor(255, 255, 255, 100)) self.mRubberBandResult.setBorderColor(QColor(0, 0, 0)) for point in pointArray: self.mRubberBandResult.addPoint(point) self.mRubberBandResult.show() self.emit(SIGNAL("outputResult"), self.area, self.mRubberBandResult) self.lineCount = 0 self.resultGeomList = [] # else: # return del selectGeom self.mRubberBand.reset(QGis.Polygon) del self.mRubberBand self.mRubberBand = None self.mDragging = False def getPointArray(self, geomList): pointArrayInner = geomList[0] pointArray1Outer = geomList[1] innerStartPoint = pointArrayInner[0] innerEndPoint = pointArrayInner[1] innerBulge = pointArrayInner[2] outerStartPoint = pointArray1Outer[0] outerEndPoint = pointArray1Outer[1] outerBulge = pointArray1Outer[2] line0 = QgsGeometry.fromPolyline([innerStartPoint, outerStartPoint]) line1 = QgsGeometry.fromPolyline([innerEndPoint, outerEndPoint]) # for i in range(1, len(pointArray0)): if line0.intersects(line1): tempPoint = outerStartPoint outerStartPoint = outerEndPoint outerEndPoint = tempPoint outerBulge = -outerBulge polylineArea = PolylineArea() polylineArea.Add(PolylineAreaPoint(innerStartPoint, innerBulge)) polylineArea.Add(PolylineAreaPoint(innerEndPoint)) polylineArea.Add(PolylineAreaPoint(outerEndPoint, -outerBulge)) polylineArea.Add(PolylineAreaPoint(outerStartPoint)) return polylineArea def makeArea(self, geomList, areaType): if areaType == ProtectionAreaType.Primary or areaType == ProtectionAreaType.Secondary: return self.makePrimaryAreaOrSecondaryArea(geomList, areaType) elif areaType == ProtectionAreaType.PrimaryAndSecondary: pointArray0 = geomList[0] pointArray1 = geomList[1] pointArray2 = geomList[2] pointArray3 = geomList[3] primaryArea = self.makePrimaryAreaOrSecondaryArea( [pointArray0, pointArray2], ProtectionAreaType.Primary) secondaryArea1 = self.makePrimaryAreaOrSecondaryArea( [pointArray0, pointArray1], ProtectionAreaType.Secondary) secondaryArea2 = self.makePrimaryAreaOrSecondaryArea( [pointArray2, pointArray3], ProtectionAreaType.Secondary) return PrimarySecondaryObstacleArea(primaryArea, secondaryArea1, secondaryArea2) # if len(geomList[0]) == 2 and len(geomList[1]) == 2 and len(geomList[2]) == 2 and len(geomList[3]) == 2: # for i in range(1, len(geomList)): # pointArray0 = geomList[0] # pointArray1 = geomList[i] # line0 = QgsGeometry.fromPolyline([pointArray0[0], pointArray1[0]]) # line1 = QgsGeometry.fromPolyline([pointArray0[len(pointArray0) - 1], pointArray1[len(pointArray1) - 1]]) # if line0.intersects(line1): # pointArray1.reverse() # pointArray0 = geomList[0] # pointArray1 = geomList[1] # pointArray2 = geomList[2] # pointArray3 = geomList[3] # area = PrimarySecondaryObstacleArea() # area.set_areas(pointArray0, pointArray1, pointArray2, pointArray3) # return area # return None return None def makePrimaryAreaOrSecondaryArea(self, geomList, areaType): pointArrayInner = geomList[0] pointArray1Outer = geomList[1] innerStartPoint = pointArrayInner[0] innerEndPoint = pointArrayInner[1] innerBulge = pointArrayInner[2] outerStartPoint = pointArray1Outer[0] outerEndPoint = pointArray1Outer[1] outerBulge = pointArray1Outer[2] line0 = QgsGeometry.fromPolyline([innerStartPoint, outerStartPoint]) line1 = QgsGeometry.fromPolyline([innerEndPoint, outerEndPoint]) # for i in range(1, len(pointArray0)): if line0.intersects(line1): tempPoint = Point3D(outerStartPoint.get_X(), outerStartPoint.get_Y()) outerStartPoint = Point3D(outerEndPoint.get_X(), outerEndPoint.get_Y()) outerEndPoint = Point3D(tempPoint.get_X(), tempPoint.get_Y()) outerBulge = -outerBulge if areaType == ProtectionAreaType.Primary: polylineArea = PolylineArea() polylineArea.Add(PolylineAreaPoint(innerStartPoint, innerBulge)) polylineArea.Add(PolylineAreaPoint(innerEndPoint)) polylineArea.Add(PolylineAreaPoint(outerEndPoint, -outerBulge)) polylineArea.Add(PolylineAreaPoint(outerStartPoint)) return PrimaryObstacleArea(polylineArea) elif areaType == ProtectionAreaType.Secondary: if innerBulge == 0 and outerBulge == 0: return SecondaryObstacleArea( innerStartPoint, innerEndPoint, outerStartPoint, outerEndPoint, MathHelper.getBearing(innerStartPoint, innerEndPoint)) elif innerBulge != 0 and outerBulge != 0: if round(innerBulge, 1) != round(outerBulge, 1): return None innerCenterPoint = MathHelper.smethod_71( innerStartPoint, innerEndPoint, innerBulge) outerCenterPoint = MathHelper.smethod_71( outerStartPoint, outerEndPoint, outerBulge) innerRadius = MathHelper.calcDistance(innerCenterPoint, innerStartPoint) outerRadius = MathHelper.calcDistance(outerCenterPoint, outerStartPoint) bearing = ( MathHelper.getBearing(innerCenterPoint, innerStartPoint) + MathHelper.getBearing(innerCenterPoint, innerEndPoint)) / 2 innerMiddlePoint = MathHelper.distanceBearingPoint( innerCenterPoint, bearing, innerRadius) if round( MathHelper.smethod_60(innerStartPoint, innerMiddlePoint, innerEndPoint), 4) != round(outerBulge, 4): bearing += 3.14159265358979 innerMiddlePoint = MathHelper.distanceBearingPoint( innerCenterPoint, bearing, innerRadius) bearing = ( MathHelper.getBearing(outerCenterPoint, outerStartPoint) + MathHelper.getBearing(outerCenterPoint, outerEndPoint)) / 2 outerMiddlePoint = MathHelper.distanceBearingPoint( outerCenterPoint, bearing, outerRadius) if round( MathHelper.smethod_60(outerStartPoint, outerMiddlePoint, outerEndPoint), 4) != round(outerBulge, 4): bearing += 3.14159265358979 outerMiddlePoint = MathHelper.distanceBearingPoint( outerCenterPoint, bearing, outerRadius) return SecondaryObstacleArea(innerStartPoint, innerMiddlePoint, innerEndPoint, outerStartPoint, None, outerMiddlePoint, outerEndPoint, innerBulge, innerBulge) return None def snapPoint(self, p, bNone=False): if define._snapping == False: return ( define._canvas.getCoordinateTransform().toMapCoordinates(p), None, None) snappingResults = self.mSnapper.snapToBackgroundLayers(p) if (snappingResults[0] != 0 or len(snappingResults[1]) < 1): if bNone: return (None, None, None) else: return (define._canvas.getCoordinateTransform(). toMapCoordinates(p), None, None) else: return (snappingResults[1][0].snappedVertex, snappingResults[1][0].snappedAtGeometry, snappingResults[1][0].layer)
class QgepMapToolConnectNetworkElements(QgsMapTool): """ This map tool connects wastewater networkelements. It works on two lists of layers: source layers with fields with a foreign key to a networkelement target layers which depict networkelements (reaches and network nodes) The tool will snap to source layers first and once one is chosen to a target layer. It will then ask which field(s) should be connected and perform the update on the database """ def __init__(self, iface, action): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.action = action self.rbline = QgsRubberBand(self.iface.mapCanvas(), QGis.Line) self.rbline.setColor(QColor('#f4530e')) self.rbline.setWidth(3) self.rbmarkers = QgsRubberBand(self.iface.mapCanvas(), QGis.Point) self.rbmarkers.setColor(QColor('#f4530e')) self.rbmarkers.setIconSize(6) self.source_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.target_snapper = QgepAreaSnapper(self.iface.mapCanvas()) self.source_feature = QgsFeature() self.rb_source_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_source_feature.setColor(QColor('#f49e79')) self.rb_source_feature.setWidth(3) self.target_feature = QgsFeature() self.rb_target_feature = QgsRubberBand(self.iface.mapCanvas()) self.rb_target_feature.setColor(QColor('#f49e79')) self.rb_target_feature.setWidth(3) def activate(self): """ Called by QGIS whenever the tool is activated. """ source_snap_layers = list() target_snap_layers = list() # A dict of layers and the fields that are foreign keys # pointing to wastewater networkelements self.network_element_sources = { QgepLayerManager.layer('vw_qgep_reach'): [ ('rp_to_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')), ('rp_from_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From')) ], QgepLayerManager.layer('od_catchment_area'): [ ('fk_wastewater_networkelement_rw_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater current')), ('fk_wastewater_networkelement_rw_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Rainwater planned')), ('fk_wastewater_networkelement_ww_current', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater current')), ('fk_wastewater_networkelement_ww_planned', QCoreApplication.translate( 'QgepMapToolConnectNetworkElements', 'Wastewater planned')) ] } # A list of layers that can be used as wastewater networkelement # targets self.network_element_targets = [ QgepLayerManager.layer('vw_wastewater_node'), QgepLayerManager.layer('vw_qgep_reach') ] for layer in self.network_element_sources.keys(): if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) source_snap_layers.append(snap_layer) for layer in self.network_element_targets: if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) target_snap_layers.append(snap_layer) self.source_snapper.setLayers(source_snap_layers) self.source_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.target_snapper.setLayers(target_snap_layers) self.target_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) self.reset() self.action.setChecked(True) self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor)) def canvasMoveEvent(self, event): """ When the mouse moves, update the rubberbands. """ pt = event.originalMapPoint() snap_match = self.snapper.snapToMap(pt) if snap_match.isValid(): if snap_match.type() != QgsPointLocator.Area: pt = snap_match.point() self.matchpoint = pt if self.source_match: if self.target_feature.id() != snap_match.featureId(): self.target_feature = self.get_feature_for_match( snap_match) self.rb_target_feature.setToGeometry( self.target_feature.geometry(), snap_match.layer()) self.rb_target_feature.show() self.rbmarkers.movePoint(pt) else: if self.source_feature.id() != snap_match.featureId(): self.source_feature = self.get_feature_for_match( snap_match) self.rb_source_feature.setToGeometry( self.source_feature.geometry(), snap_match.layer()) self.rb_source_feature.show() self.rbmarkers.movePoint(pt, 0) self.rbmarkers.show() else: self.rbmarkers.hide() if self.source_match: self.rb_target_feature.hide() else: self.rb_source_feature.hide() self.rbline.movePoint(pt) self.snapresult = snap_match def canvasReleaseEvent(self, event): """ On a click update the rubberbands and the snapping results if it's a left click. Reset if it's a right click. """ if event.button() == Qt.LeftButton: if self.snapresult.isValid(): if self.source_match: self.connect_features(self.source_match, self.snapresult) else: self.rbline.show() self.rbline.addPoint(self.matchpoint) self.source_match = self.snapresult self.snapper = self.target_snapper else: self.reset() def deactivate(self): """ Called by QGIS whenever this tool is deactivated. """ self.reset() self.action.setChecked(False) def reset(self): """ Resets the tool to a pristine state """ self.source_match = None self.rbline.hide() self.rbline.reset() self.rbmarkers.hide() self.rbmarkers.reset(QGis.Point) self.rbmarkers.addPoint(QgsPoint()) self.snapresult = None self.source_match = None self.snapper = self.source_snapper self.source_feature = QgsFeature() self.target_feature = QgsFeature() self.rb_source_feature.reset() self.rb_target_feature.reset() def get_feature_for_match(self, match): """ Get the feature for a snapping result @param match: The QgsPointLocator.SnapMatch object @return: A feature """ return next(match.layer().getFeatures(QgsFeatureRequest().setFilterFid(match.featureId()))) def connect_features(self, source, target): """ Connects the source feature with the target feature. @param source: A QgsPointLocator.Match object. Its foreign key will be updated. A dialog will be opened which asks the user for which foreign key(s) he wants to update. @param target: A QgsPointLocator.Match object. This feature will be used as link target. Its obj_id attribute will be used as primary key. """ dlg = QDialog(self.iface.mainWindow()) dlg.setWindowTitle(self.tr('Select properties to connect')) dlg.setLayout(QFormLayout()) properties = list() for prop in self.network_element_sources[source.layer()]: cbx = QCheckBox(prop[1]) cbx.setObjectName(prop[0]) properties.append(cbx) dlg.layout().addWidget(cbx) btn_box = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(btn_box) btn_box.accepted.connect(dlg.accept) btn_box.rejected.connect(dlg.reject) source_feature = self.get_feature_for_match(source) target_feature = self.get_feature_for_match(target) if dlg.exec_(): for cbx in properties: if cbx.isChecked(): source_feature[cbx.objectName()] = target_feature['obj_id'] if source.layer().updateFeature(source_feature): self.iface.messageBar().pushMessage('QGEP', self.tr('Connected {} to {}').format( source_feature[ 'identifier'], target_feature['identifier']), QgsMessageBar.INFO, 5) else: self.iface.messageBar().pushMessage('QGEP', self.tr( 'Error connecting features'), QgsMessageBar.WARNING, 5) self.reset()
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) if hasattr(self.canvas, 'setParallelRenderingEnabled'): self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) gpsspacewidget= QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = QgsRubberBand(self.canvas) self.currentfeatureband.setIconSize(20) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 76)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(0, 0, 212, 76)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.featureformloaded.connect(self.feature_form_loaded) self.connectButtons() def init_qgisproject(self, doc): parser = ProjectParser(doc) canvasnode = parser.canvasnode self.canvas.freeze() self.canvas.mapRenderer().readXML(canvasnode) self.canvaslayers = parser.canvaslayers() self.canvas.setLayerSet(self.canvaslayers) #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0]; #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0]; #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0]; #color = QColor(red, green, blue); #self.canvas.setCanvasColor(color) self.canvas.updateScale() return self.canvas.mapRenderer().destinationCrs() def showEvent(self, *args, **kwargs): if self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, project, editmode): self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0, 200)) band.setIconSize(20) band.setWidth(2) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) for feature in features: band.addGeometry(feature.geometry(), layer) def highlight_active_selection(self, layer, feature, features): self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) def clear_selection(self): # Clear the main selection rubber band self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() break self.editfeaturestack.append((form, feature)) self.load_form(form) trigger_default_action() def clear_temp_objects(self): def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position) def gps_first_fix(self, postion, gpsinfo): zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): self.marker.hide() def select_data_entry(self): def showformerror(form): pass def actions(): for form in self.project.forms: action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format(form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form") formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): self.project = project self.actionPan.trigger() try: firstform = project.forms[0] self.load_form(firstform) self.dataentryselection.setVisible(True) except IndexError: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.freeze(False) self.canvas.refresh() def setMapTool(self, tool, *args): self.canvas.setMapTool(tool) def connectButtons(self): def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24,24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/info')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def load_form(self, form): self.clearCapatureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) def create_capture_buttons(self, form): tool = form.getMaptool()(self.canvas) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(partial(self.showUIMessage, form.label)) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass layer = form.QGISLayer fields = layer.pendingFields() feature = QgsFeature(fields) feature.setGeometry(geometry) for index in xrange(fields.count()): pkindexes = layer.dataProvider().pkAttributeIndexes() if index in pkindexes and layer.dataProvider().name() == 'spatialite': continue value = layer.dataProvider().defaultValue(index) feature[index] = value RoamEvents.open_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) def clearCapatureTools(self): captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ if not self.canvaslayers: return #Freeze the canvas to save on UI refresh self.canvas.freeze() for layer in self.canvaslayers: if layer.layer().type() == QgsMapLayer.RasterLayer: layer.setVisible(not layer.isVisible()) # Really!? We have to reload the whole layer set every time? # WAT? self.canvas.setLayerSet(self.canvaslayers) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCapatureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class MoveTool(QgsMapToolAdvancedDigitizing): """ Map tool class to move or copy an object """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__findVertex = False self.__onMove = False self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def deactivate(self): """ When the action is deselected """ self.__cancel() QgsMapToolAdvancedDigitizing.deactivate(self) def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def __cancel(self): """ To cancel used variables """ if self.__rubberBand is not None: self.canvas().scene().removeItem(self.__rubberBand) self.__rubberBand.reset() self.__rubberBand = None if self.__rubberSnap is not None: self.canvas().scene().removeItem(self.__rubberSnap) self.__rubberSnap.reset() self.__rubberSnap = None self.__isEditing = False self.__findVertex = False self.__onMove = False self.__lastFeatureId = None self.__selectedFeature = None self.__confDlg = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.__removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in range(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.curveToLine()), None) @staticmethod def __newCurve(curved, line_v2, dx, dy): """ To create a new moved line :param curved: if the line is curved :param line_v2: the original line :param dx: x translation :param dy: y translation :return: the new line """ if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in range(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2(self.__selectedFeature.geometry(), self.__iface) vertex = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in range(polygon_v2.numInteriorRings()): line_v2 = self.__newCurve(curved[num+1], polygon_v2.interiorRing(num), dx, dy) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __onConfirmCancel(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__confDlg.accept() self.__cancel() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute(field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \ QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__confDlg.accept() self.__cancel() def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and not self.__onMove: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings]) if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: if self.__rubberBand is not None: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry(QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(map_point) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(map_point) else: self.__pointPreview(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(8) if self.__rubberSnap is not None: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(map_point, self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) else: self.__rubberSnap.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self) if intersection is not None: self.__rubberSnap.setIcon(1) point = intersection else: self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Select vertex for moving (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.__findVertex = True self.setMode(self.CaptureLine) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) else: self.setMode(self.CaptureNone) self.__onMove = True elif self.__findVertex: self.__findVertex = False self.setMode(self.CaptureNone) closest = self.__selectedFeature.geometry().closestVertex(event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = True elif self.__onMove: self.__onMove = False mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections(mapPoint, self.canvas(), self) if intersection is not None: mapPoint = intersection self.__isEditing = True if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show()
class VoGISProfilToolMainDialog(QDialog): def __init__(self, interface, settings): self.settings = settings self.iface = interface self.selectingVisibleRasters = False self.thread = None QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolMain() self.ui.setupUi(self) if self.settings.onlyHektoMode is True: self.ui.IDC_widRaster.hide() self.adjustSize() validator = QIntValidator(-32768, 32768, self) self.ui.IDC_tbNoDataExport.setValidator(validator) self.ui.IDC_tbFromX.setText('-30000') self.ui.IDC_tbFromY.setText('240000') self.ui.IDC_tbToX.setText('-20000') self.ui.IDC_tbToY.setText('230000') self.__addRastersToGui() self.__addPolygonsToGui() for line_lyr in self.settings.mapData.lines.lines(): self.ui.IDC_cbLineLayers.addItem(line_lyr.name, line_lyr) if self.settings.mapData.lines.count() < 1: self.ui.IDC_rbDigi.setChecked(True) self.ui.IDC_rbShapeLine.setEnabled(False) #Einstellungen fuer Linie zeichen self.action = QAction(QIcon(":/plugins/vogisprofiltoolmain/icons/icon.png"), "VoGIS-Profiltool", self.iface.mainWindow()) self.action.setWhatsThis("VoGIS-Profiltool") self.canvas = self.iface.mapCanvas() self.tool = ProfiletoolMapTool(self.canvas, self.action) self.savedTool = self.canvas.mapTool() self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) if QGis.QGIS_VERSION_INT >= 10900: #self.rubberband.setBrushStyle() self.rubberband.setLineStyle(Qt.SolidLine) self.rubberband.setWidth(4.0) self.rubberband.setColor(QColor(0, 255, 0)) #http://www.qgis.org/api/classQgsRubberBand.html#a6f7cdabfcf69b65dfc6c164ce2d01fab self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def accept(self): try: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "ACCEPTED") #QgsMessageLog.logMessage('nodata: {0}'.format(self.settings.nodata_value), 'VoGis') self.settings.nodata_value = int(self.ui.IDC_tbNoDataExport.text()) QgsMessageLog.logMessage('maindlg: nodata: {0}'.format(self.settings.nodata_value), 'VoGis') if self.settings.onlyHektoMode is True and self.settings.mapData.rasters.count() > 0: self.settings.onlyHektoMode = False if self.settings.onlyHektoMode is False: if self.settings.mapData.rasters.count() < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", u"Keine Raster vorhanden. Zum Hektometrieren Dialog neu öffnen.") #return retVal = QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Rasterebene vorhanden oder sichtbar! Nur hektometrieren?', None, QApplication.UnicodeUTF8), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if retVal == QMessageBox.No: return else: self.settings.onlyHektoMode = True self.settings.createHekto = True if self.__getSettingsFromGui() is False: return if self.settings.onlyHektoMode is False: if len(self.settings.mapData.rasters.selectedRasters()) < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "Kein Raster selektiert!") #msg = #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", msg) QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Kein Raster selektiert!', None, QApplication.UnicodeUTF8)) return QgsMessageLog.logMessage('modeLine!=line: {0}'.format(self.settings.modeLine != enumModeLine.line), 'VoGis') QgsMessageLog.logMessage('customLine is None: {0}'.format(self.settings.mapData.customLine is None), 'VoGis') if self.settings.modeLine != enumModeLine.line and self.settings.mapData.customLine is None: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Profillinie vorhanden!', None, QApplication.UnicodeUTF8)) return if len(self.settings.mapData.polygons.selected_polygons()) > 0 and len(self.settings.mapData.rasters.selectedRasters()) > 1: raster_names = list(raster.name for raster in self.settings.mapData.rasters.selectedRasters()) sel_raster, ok_clicked = QInputDialog.getItem( self.iface.mainWindow(), u'DHM?', u'Welches DHM soll zur Flächenverschneidung verwendet werden?', raster_names, 0, False ) if ok_clicked is False: return self.settings.intersection_dhm_idx = raster_names.index(sel_raster) #self.rubberband.reset(self.polygon) #QDialog.accept(self) QApplication.setOverrideCursor(Qt.WaitCursor) create_profile = CreateProfile(self.iface, self.settings) thread = QThread(self) create_profile.moveToThread(thread) create_profile.finished.connect(self.profiles_finished) create_profile.error.connect(self.profiles_error) create_profile.progress.connect(self.profiles_progress) thread.started.connect(create_profile.create) thread.start(QThread.LowestPriority) self.thread = thread self.create_profile = create_profile self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) except: QApplication.restoreOverrideCursor() ex = u'{0}'.format(traceback.format_exc()) msg = 'Unexpected ERROR:\n\n{0}'.format(ex[:2000]) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", msg) def profiles_finished(self, profiles, intersections): QApplication.restoreOverrideCursor() self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) #self.create_profile.deleteLater() self.thread.quit() self.thread.wait() #self.thread.deleteLater() #QGIS 2.0 http://gis.stackexchange.com/a/58754 http://gis.stackexchange.com/a/57090 self.iface.mainWindow().statusBar().showMessage('VoGIS-Profiltool, {0} Profile'.format(len(profiles))) QgsMessageLog.logMessage(u'Profile Count: {0}'.format(len(profiles)), 'VoGis') if len(profiles) < 1: QApplication.restoreOverrideCursor() QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Es konnten keine Profile erstellt werden.', None, QApplication.UnicodeUTF8)) return dlg = VoGISProfilToolPlotDialog(self.iface, self.settings, profiles, intersections) dlg.show() #result = self.dlg.exec_() dlg.exec_() def profiles_error(self, exception_string): QApplication.restoreOverrideCursor() QgsMessageLog.logMessage(u'Error during profile creation: {0}'.format(exception_string), 'VoGis') QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", exception_string) def profiles_progress(self, msg): self.iface.mainWindow().statusBar().showMessage(msg) self.ui.IDC_lblCreateStatus.setText(msg) #QgsMessageLog.logMessage(msg, 'VoGis') QApplication.processEvents() def reject(self): if not self.thread is None: if self.thread.isRunning(): self.create_profile.abort() return self.rubberband.reset(self.polygon) QDialog.reject(self) def selectVisibleRasters(self): self.refreshRasterList() self.selectingVisibleRasters = True extCanvas = self.iface.mapCanvas().extent() #alle raster in den einstellunge deselektieren for r in self.settings.mapData.rasters.rasters(): r.selected = False #alle raster in der ListView deselektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) item.setCheckState(Qt.Unchecked) #Raster im Extent selektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) if QGis.QGIS_VERSION_INT < 10900: raster = item.data(Qt.UserRole).toPyObject() else: raster = item.data(Qt.UserRole) for r in self.settings.mapData.rasters.rasters(): if extCanvas.intersects(r.grid.extent()): if r.id == raster.id: r.selected = True item.setCheckState(Qt.Checked) self.selectingVisibleRasters = False def lineLayerChanged(self, idx): if self.ui.IDC_rbShapeLine.isChecked() is False: self.ui.IDC_rbShapeLine.setChecked(True) if QGis.QGIS_VERSION_INT < 10900: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex()).toPyObject()) else: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) lyr = lineLyr.line #QgsMessageLog.logMessage('{0}'.format(lyr.selectedFeatureCount()), 'VoGis') #QgsMessageLog.logMessage('{0}'.format(dir(lyr)), 'VoGis') if hasattr(lyr, 'selectedFeatureCount'): if(lyr.selectedFeatureCount() < 1): self.ui.IDC_chkOnlySelectedFeatures.setChecked(False) else: self.ui.IDC_chkOnlySelectedFeatures.setChecked(True) def valueChangedEquiDistance(self, val): if self.ui.IDC_rbEquiDistance.isChecked() is False: self.ui.IDC_rbEquiDistance.setChecked(True) def valueChangedVertexCount(self, val): if self.ui.IDC_rbVertexCount.isChecked() is False: self.ui.IDC_rbVertexCount.setChecked(True) def lvRasterItemChanged(self, item): if self.selectingVisibleRasters is True: return if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: raster_lyr = item_data.toPyObject() else: raster_lyr = item_data self.settings.mapData.rasters.getById(raster_lyr.id).selected = selected def lvPolygonItemChanged(self, item): if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: poly_lyr = item_data.toPyObject() else: poly_lyr = item_data self.settings.mapData.polygons.getById(poly_lyr.id).selected = selected def refreshRasterList(self): legend = self.iface.legendInterface() avail_lyrs = legend.layers() raster_coll = RasterCollection() for lyr in avail_lyrs: if legend.isLayerVisible(lyr): lyr_type = lyr.type() lyr_name = unicodedata.normalize('NFKD', unicode(lyr.name())).encode('ascii', 'ignore') if lyr_type == 1: if lyr.bandCount() < 2: new_raster = Raster(lyr.id(), lyr_name, lyr) raster_coll.addRaster(new_raster) self.settings.mapData.rasters = raster_coll self.__addRastersToGui() def __addRastersToGui(self): self.ui.IDC_listRasters.clear() check = Qt.Unchecked if self.settings.mapData.rasters.count() == 1: check = Qt.Checked self.settings.mapData.rasters.rasters()[0].selected = True for raster_lyr in self.settings.mapData.rasters.rasters(): item = QListWidgetItem(raster_lyr.name) item.setData(Qt.UserRole, raster_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listRasters.addItem(item) def __addPolygonsToGui(self): self.ui.IDC_listPolygons.clear() check = Qt.Unchecked for poly_lyr in self.settings.mapData.polygons.polygons(): item = QListWidgetItem(poly_lyr.name) item.setData(Qt.UserRole, poly_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listPolygons.addItem(item) def drawLine(self): if self.ui.IDC_rbDigi.isChecked() is False: self.ui.IDC_rbDigi.setChecked(True) self.dblckltemp = None self.rubberband.reset(self.polygon) self.__cleanDigi() self.__activateDigiTool() self.canvas.setMapTool(self.tool) def __createDigiFeature(self, pnts): u = Util(self.iface) f = u.createQgLineFeature(pnts) self.settings.mapData.customLine = f def __lineFinished(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) self.pointsToDraw.append(newPoint) #launch analyses self.iface.mainWindow().statusBar().showMessage(str(self.pointsToDraw)) if len(self.pointsToDraw) < 2: self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint self.drawnLine = None QMessageBox.warning(self, "VoGIS-Profiltool", QApplication.translate('code', 'Profillinie digitalisieren abgebrochen!', None, QApplication.UnicodeUTF8)) self.drawnLine = self.__createDigiFeature(self.pointsToDraw) self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint def __cleanDigi(self): self.pointsToDraw = [] self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.savedTool) def __activateDigiTool(self): QObject.connect(self.tool, SIGNAL("moved"), self.__moved) QObject.connect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.connect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.connect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) QObject.connect(self.tool, SIGNAL("deactivate"), self.__deactivateDigiTool) def __deactivateDigiTool(self): QObject.disconnect(self.tool, SIGNAL("moved"), self.__moved) QObject.disconnect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.disconnect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.disconnect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) if QGis.QGIS_VERSION_INT < 10900: self.iface.mainWindow().statusBar().showMessage(QString("")) else: self.iface.mainWindow().statusBar().showMessage('') def __moved(self, position): if len(self.pointsToDraw) > 0: mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) self.rubberband.reset(self.polygon) newPnt = QgsPoint(mapPos.x(), mapPos.y()) if QGis.QGIS_VERSION_INT < 10900: for i in range(0, len(self.pointsToDraw)): self.rubberband.addPoint(self.pointsToDraw[i]) self.rubberband.addPoint(newPnt) else: pnts = self.pointsToDraw + [newPnt] self.rubberband.setToGeometry(QgsGeometry.fromPolyline(pnts),None) def __rightClicked(self, position): self.__lineFinished(position) def __leftClicked(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) #if self.selectionmethod == 0: if newPoint == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointsToDraw) == 0: self.rubberband.reset(self.polygon) self.pointsToDraw.append(newPoint) def __doubleClicked(self, position): pass #not in use right now def __lineCancel(self): pass def __getSettingsFromGui(self): self.settings.linesExplode = (self.ui.IDC_chkLinesExplode.checkState() == Qt.Checked) self.settings.linesMerge = (self.ui.IDC_chkLinesMerge.checkState() == Qt.Checked) self.settings.onlySelectedFeatures = (self.ui.IDC_chkOnlySelectedFeatures.checkState() == Qt.Checked) self.settings.equiDistance = self.ui.IDC_dblspinDistance.value() self.settings.vertexCnt = self.ui.IDC_dblspinVertexCnt.value() #self.settings.createHekto = (self.ui.IDC_chkCreateHekto.checkState() == Qt.Checked) self.settings.nodesAndVertices = (self.ui.IDC_chkNodesAndVertices.checkState() == Qt.Checked) if QGis.QGIS_VERSION_INT < 10900: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex() ).toPyObject() ) else: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) if self.settings.onlySelectedFeatures is True and self.settings.mapData.selectedLineLyr.line.selectedFeatureCount() < 1: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', u'Der gewählte Layer hat keine selektierten Elemente.', None, QApplication.UnicodeUTF8)) return False if self.ui.IDC_rbDigi.isChecked(): self.settings.modeLine = enumModeLine.customLine elif self.ui.IDC_rbShapeLine.isChecked(): self.settings.modeLine = enumModeLine.line else: #self.ui.IDC_rbStraigthLine self.settings.modeLine = enumModeLine.straightLine if self.ui.IDC_rbEquiDistance.isChecked(): self.settings.modeVertices = enumModeVertices.equiDistant else: self.settings.modeVertices = enumModeVertices.vertexCnt if self.ui.IDC_rbStraigthLine.isChecked(): ut = Util(self.iface) if ut.isFloat(self.ui.IDC_tbFromX.text(), QApplication.translate('code', 'Rechtswert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromX = float(self.ui.IDC_tbFromX.text()) if ut.isFloat(self.ui.IDC_tbFromY.text(), QApplication.translate('code', 'Hochwert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromY = float(self.ui.IDC_tbFromY.text()) if ut.isFloat(self.ui.IDC_tbToX.text(), QApplication.translate('code', 'Rechtswert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toX = float(self.ui.IDC_tbToX.text()) if ut.isFloat(self.ui.IDC_tbToY.text(), QApplication.translate('code', 'Hochwert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toY = float(self.ui.IDC_tbToY.text()) fromPnt = QgsPoint(fromX, fromY) toPnt = QgsPoint(toX, toY) self.settings.mapData.customLine = ut.createQgLineFeature([fromPnt, toPnt]) return True
class RectangleMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setColor(QColor(255, 0, 0, 180)) self.rubberBand.setWidth(1) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.emit(SIGNAL("rectangleCreated()")) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(QGis.Polygon) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint == None or self.endPoint == None: return None #elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): # return None return QgsRectangle(self.startPoint, self.endPoint) def setRectangle(self, rect): if rect == self.rectangle(): return False if rect == None: self.reset() else: self.startPoint = QgsPoint(rect.xMaximum(), rect.yMaximum()) self.endPoint = QgsPoint(rect.xMinimum(), rect.yMinimum()) self.showRect(self.startPoint, self.endPoint) return True
class EditTool(MapTool): """ Inspection tool which copies the feature to a new layer and copies selected data from the underlying feature. """ finished = pyqtSignal(object, QgsFeature) featuresfound = pyqtSignal(dict) def __init__(self, canvas, forms, snapradius = 2): MapTool.__init__(self, canvas, []) self.canvas = canvas self.radius = snapradius self.forms = forms self.band = QgsRubberBand(self.canvas) self.band.setColor(QColor.fromRgb(224,162,16)) self.band.setWidth(3) self.selectband = None self.selectrect = QRect() self.dragging = False self.cursor = QCursor(QPixmap(["16 16 3 1", " c None", ". c #FF0000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ "])) def addForm(self, form): self.forms.append(form) self.layersupdated.emit(True) def layers(self): """ Return a set of layers that this edit tool can work on """ return set([form.QGISLayer for form in self.forms]) def formsforlayer(self, layer): for form in self.forms: if form.QGISLayer == layer: yield form def reset(self): self.forms = [] self.layersupdated.emit(False) def getFeatures(self, rect): rq = QgsFeatureRequest().setFilterRect(rect) self.band.reset() for layer in self.layers(): forms = list(self.formsforlayer(layer)) rq = QgsFeatureRequest().setFilterRect(rect) for feature in layer.getFeatures(rq): if feature.isValid(): yield feature, forms def toSearchRect(self, point): searchRadius = self.canvas.extent().width() * ( self.radius / 100.0 ) point = self.toMapCoordinates(point) rect = QgsRectangle() rect.setXMinimum(point.x() - searchRadius) rect.setXMaximum(point.x() + searchRadius) rect.setYMinimum(point.y() - searchRadius) rect.setYMaximum(point.y() + searchRadius) return rect def canvasPressEvent(self, event): self.selectrect.setRect( 0, 0, 0, 0 ) self.selectband = QgsRubberBand(self.canvas, QGis.Polygon ) self.selectband.setColor(QColor.fromRgb(0,0,255, 65)) self.selectband.setWidth(5) def canvasMoveEvent(self, event): if not event.buttons() == Qt.LeftButton: return if not self.dragging: self.dragging = True self.selectrect.setTopLeft(event.pos()) self.selectrect.setBottomRight(event.pos()) maptoolutils.setRubberBand(self.canvas, self.selectrect, self.selectband) def canvasReleaseEvent(self, event): if self.dragging: geometry = self.selectband.asGeometry() if not geometry: return rect = geometry.boundingBox() else: rect = self.toSearchRect(event.pos()) self.dragging = False self.selectband.reset() features = dict(self.getFeatures(rect)) print features if len(features) == 1: feature = features.keys()[0] forms = features.values()[0] if len(forms) == 1: self.finished.emit(forms[0], feature) else: self.featuresfound.emit(features) elif len(features) > 0: self.featuresfound.emit(features) def isEditTool(self): return True
class QgepProfileMapTool( QgepMapTool ): ''' The map tool used for PROFILE Allows to find the shortest path between several nodes. ''' profileChanged = pyqtSignal( object ) profile = QgepProfile() segmentOffset = 0 selectedPathPoints = [] pathPolyline = [] def __init__( self, canvas, button, networkAnalyzer ): QgepMapTool.__init__(self, canvas, button) settings = QSettings() helperLineColor = settings.value( "/QGEP/HelperLineColor", u'#FFD900' ) highlightColor = settings.value( "/QGEP/HighlightColor", u'#40FF40' ) self.networkAnalyzer = networkAnalyzer # Init rubberband to visualize current status self.rbHelperLine = QgsRubberBand( self.canvas ) self.rbHelperLine.setColor( QColor( helperLineColor ) ) self.rbHelperLine.setWidth( 2 ) self.rbHighlight = QgsRubberBand( self.canvas ) self.rbHighlight.setColor( QColor( highlightColor ) ) self.rbHighlight.setWidth( 5 ) self.profile.setRubberband( self.rbHighlight ) def setActive( self ): ''' activates this map tool ''' self.saveTool = self.canvas.mapTool() self.canvas.setMapTool( self ) def deactivate(self): ''' Called whenever this map tool is deactivated. Used to clean up code ''' QgepMapTool.deactivate(self) self.rubberBand.reset() self.rbHelperLine.reset() self.selectedPathPoints = [] self.pathPolyline = [] def findPath( self, pStart, pEnd ): ''' Tries to find the shortest path between pStart and pEnd. If it finds a path: * The path is visualized with a QgsRubberband * The profile plot is updated to represent the current path @param pStart: The id of the start point of the path @param pEnd: The id of the end point of the path ''' backupCursor = self.canvas.cursor() self.canvas.setCursor(Qt.WaitCursor) #try: ( vertices, edges ) = self.networkAnalyzer.shortestPath( pStart, pEnd ) self.appendProfile( vertices, edges ) # except: # pass self.canvas.setCursor(backupCursor) if len( vertices ) > 0: return True else: return False def appendProfile( self, vertices, edges ): ''' Appends to the current profile @param vertices: A collection of vertices to append @param edges: A collection of edges which connect the vertices ''' self.logger.debug( 'Append profile' ) self.logger.info( ' * ' + `len( vertices )` + ' vertices' ) for v in vertices: self.logger.debug( ' *' + `v`) self.logger.info( ' * ' + `len( edges )` + ' edges') for e in edges: self.logger.debug( ' *' + `e`) # Fetch all the needed edges in one batch edgeLayer = self.networkAnalyzer.getReachLayer() edgeAttrs = edgeLayer.dataProvider().attributeIndexes() edgeIds = [edge['feature'] for p1, p2, edge in edges] edgeFeatures = self.networkAnalyzer.getFeaturesById(edgeLayer, edgeAttrs, edgeIds, True) # We need some additional nodes, where we need to interpolate... interpolateNodesFrom = [edgeFeatures.attrAsUnicode( feat, u'from_obj_id_interpolate' ) for feat in edgeFeatures.asDict().values()] interpolateNodesTo = [edgeFeatures.attrAsUnicode( feat, u'to_obj_id_interpolate' ) for feat in edgeFeatures.asDict().values()] additionalIds = [self.networkAnalyzer.vertexIds[node] for node in interpolateNodesFrom] additionalIds += [self.networkAnalyzer.vertexIds[node] for node in interpolateNodesTo] # Now, fetch the nodes we need nodeLayer = self.networkAnalyzer.getNodeLayer() nodeIds = vertices + additionalIds nodeAttrs = nodeLayer.dataProvider().attributeIndexes() nodeFeatures = self.networkAnalyzer.getFeaturesById(nodeLayer, nodeAttrs, nodeIds, False) if len( vertices ) > 1: self.rubberBand.reset() elem = QgepProfileNodeElement( vertices[0], nodeFeatures, 0 ) self.profile.addElement( vertices[0], elem ) for p1, p2, edge in edges: fromOffset = self.segmentOffset toOffset = self.segmentOffset + edge['weight'] if 'reach' == edge['objType']: if self.profile.hasElement( edge['baseFeature'] ): self.profile[edge['baseFeature']].addSegment( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) else: elem = QgepProfileReachElement( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) self.profile.addElement(elem.objId, elem) elif 'special_structure' == edge['objType']: if self.profile.hasElement( edge['baseFeature'] ): self.profile[edge['baseFeature']].addSegment( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) else: elem = QgepProfileSpecialStructureElement( p1, p2, edge['feature'], nodeFeatures, edgeFeatures, fromOffset, toOffset ) self.profile.addElement(elem.objId, elem) elem = QgepProfileNodeElement( p2, nodeFeatures, toOffset ) self.profile.addElement(p2, elem) self.segmentOffset = toOffset self.profileChanged.emit( self.profile ) # Create rubberband geometry for featId in edgeIds: self.pathPolyline.extend( edgeFeatures[featId].geometry().asPolyline() ) self.rubberBand.addGeometry( QgsGeometry.fromPolyline( self.pathPolyline ), nodeLayer ) self.profileChanged.emit( self.profile ) return True else: return False def mouseMoved( self, event ): ''' Mouse moved: update helper line @param event: The mouse event with coordinates and all ''' if len( self.selectedPathPoints ) > 0: self.rbHelperLine.reset() for point in self.selectedPathPoints: self.rbHelperLine.addPoint( point[1] ) mousePos = self.canvas.getCoordinateTransform().toMapCoordinates( event.pos().x(), event.pos().y() ) self.rbHelperLine.addPoint( mousePos ) def rightClicked( self, event ): ''' Cancel any ongoing path selection @param event: The mouse event with coordinates and all ''' self.selectedPathPoints = [] self.pathPolyline = [] self.rbHelperLine.reset() self.profile.reset() self.segmentOffset = 0 def leftClicked( self, event ): ''' Select startpoint / intermediate point / endpoint @param event: The mouse event with coordinates and all ''' snappedPoint = self.networkAnalyzer.snapPoint( event ) if snappedPoint is not None: if len( self.selectedPathPoints ) > 0: pf = self.findPath( self.selectedPathPoints[-1][0], snappedPoint.snappedAtGeometry ) if pf: self.selectedPathPoints.append( ( snappedPoint.snappedAtGeometry, QgsPoint( snappedPoint.snappedVertex ) ) ) else: msg = self.msgBar.createMessage( 'No path found' ) self.msgBar.pushWidget( msg, QgsMessageBar.WARNING ) else: self.selectedPathPoints.append( ( snappedPoint.snappedAtGeometry, QgsPoint( snappedPoint.snappedVertex ) ) )
class CaptureCoordinateToolUpdate(QgsMapTool): def __init__(self, canvas, dataBaseProcedureData0, point3d0, procEntityListType0): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.mSnapper = QgsMapCanvasSnapper(canvas) self.rubberBand = QgsRubberBand(canvas, QGis.Point) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(10) self.rubberBandClick = QgsRubberBand(canvas, QGis.Point) self.rubberBandClick.setColor(Qt.green) self.rubberBandClick.setWidth(3) self.obstaclesLayerList = QgisHelper.getSurfaceLayers( SurfaceTypes.Obstacles) self.demLayerList = QgisHelper.getSurfaceLayers(SurfaceTypes.DEM) self.reset() self.dataBaseProcedureData_0 = dataBaseProcedureData0 self.point3d_0 = point3d0 self.procEntityListType_0 = procEntityListType0 def reset(self): self.Point = None def canvasReleaseEvent(self, e): pointBackground = e.pos() self.Point, self.pointID, self.layer = self.snapPoint(e.pos()) self.selectedLayerFromSnapPoint = None resultValueList = [] self.rubberBandClick.reset(QGis.Point) self.rubberBandClick.addPoint(self.Point) self.rubberBandClick.show() if self.obstaclesLayerList != None: for obstacleLayer in self.obstaclesLayerList: if self.layer == None: break if obstacleLayer.name() == self.layer.name(): self.selectedLayerFromSnapPoint = self.layer break # itemList = [] if self.selectedLayerFromSnapPoint != None: dataProvider = self.selectedLayerFromSnapPoint.dataProvider() featureIter = dataProvider.getFeatures( QgsFeatureRequest(self.pointID)) feature = None for feature0 in featureIter: feature = feature0 idx = self.selectedLayerFromSnapPoint.fieldNameIndex('Name') idValue = feature.attributes()[idx] resultValueList.append(idValue.toString()) resultValueList.append(str(self.Point.x())) resultValueList.append(str(self.Point.y())) idx = self.selectedLayerFromSnapPoint.fieldNameIndex('Altitude') altitudeValue = feature.attributes()[idx] resultValueList.append(altitudeValue.toString()) else: if self.Point != None: identifyResult = None idValue = "Background" if self.demLayerList != None: for demLayer in self.demLayerList: identifyResults = demLayer.dataProvider().identify( self.Point, QgsRaster.IdentifyFormatValue) identifyResult = identifyResults.results() if identifyResult != None and identifyResult[1].toString( ) != "": idValue = "DEM" resultValueList.append(idValue) resultValueList.append(str(self.Point.x())) resultValueList.append(str(self.Point.y())) if identifyResult != None: resultValueList.append(identifyResult[1].toString()) else: resultValueList.append("0") self.point3d_0 = Point3D(self.Point.x(), self.Point.y()) self.emit(SIGNAL("resultPointValueList"), resultValueList, self.dataBaseProcedureData_0, self.point3d_0, self.procEntityListType_0, self) def canvasMoveEvent(self, e): if define._snapping == False: return self.rubberBand.reset(QGis.Point) # snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas, True) snapPoint, snapPointID, layer = self.snapPoint(e.pos(), True) if snapPoint == None: return self.rubberBand.addPoint(snapPoint) self.rubberBand.show() # print snapPointID def snapPoint(self, p, bNone=False): if define._snapping == False: return ( define._canvas.getCoordinateTransform().toMapCoordinates(p), None, None) snappingResults = self.mSnapper.snapToBackgroundLayers(p) if (snappingResults[0] != 0 or len(snappingResults[1]) < 1): if bNone: return (None, None, None) else: return (define._canvas.getCoordinateTransform(). toMapCoordinates(p), None, None) else: return (snappingResults[1][0].snappedVertex, snappingResults[1][0].snappedAtGeometry, snappingResults[1][0].layer) def deactivate(self): self.rubberBand.reset(QGis.Point) QgsMapTool.deactivate(self) self.emit(SIGNAL("deactivated()"))
class DsgLineTool(QgsMapTool): lineCreated = pyqtSignal(QgsGeometry) def __init__(self, canvas): """ Constructor """ super(DsgLineTool, self).__init__(canvas) self.canvas = canvas self.rubberBand = None self.reset() def deactivate(self): """ Deativates this tool """ self.canvas.scene().removeItem(self.rubberBand) super(DsgLineTool, self).deactivate() def defineRubberBand(self): """ Defines the rubber band style """ settings = QSettings() myRed = int(settings.value("/qgis/default_measure_color_red", 222)) myGreen = int(settings.value("/qgis/default_measure_color_green", 155)) myBlue = int(settings.value("/qgis/default_measure_color_blue", 67)) self.rubberBand = QgsRubberBand(self.canvas) self.rubberBand.setColor(QColor(myRed, myGreen, myBlue, 100)) self.rubberBand.setWidth(3) def reset(self): """ Resets the tool """ if self.rubberBand: self.rubberBand.reset(QGis.Line) self.isEmittingPoint = False self.defineRubberBand() def canvasPressEvent(self, e): """ Reimplementation to add a point to the rubber band or reset it """ if self.isEmittingPoint: point = self.snapPoint(e.pos()) self.rubberBand.addPoint(point, True) else: self.reset() self.isEmittingPoint = True def canvasReleaseEvent(self, e): """ Reimplementation to add a vertex to the rubber band or to finish the rubber band according to the button used """ point = self.snapPoint(e.pos()) if e.button() == Qt.RightButton: geom = self.rubberBand.asGeometry() self.reset() self.lineCreated.emit(geom) elif e.button() == Qt.LeftButton: self.isEmittingPoint = True self.rubberBand.addPoint(point, True) def canvasMoveEvent(self, e): """ Reimplementation to move the rubber band """ if not self.isEmittingPoint: return point = self.snapPoint(e.pos()) self.rubberBand.movePoint(point) def snapPoint(self, p): """ Reimplementation to make use of the snap """ m = self.canvas.snappingUtils().snapToMap(p) if m.isValid(): return m.point() else: return self.canvas.getCoordinateTransform().toMapCoordinates(p)
class MoveRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandDisplacement = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.isLayerVisible = True self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.originalCenter = self.layer.center # this tool do the displacement itself TODO update so it is done by # transformed coordinates + new center) self.originalCornerPoints = self.layer.transformedCornerCoordinates( *self.layer.transformParameters()) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) self.showDisplacement(self.startPoint, self.endPoint) self.layer.history.append({ "action": "move", "center": self.layer.center }) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() x = self.originalCenter.x() + self.endPoint.x() - self.startPoint.x() y = self.originalCenter.y() + self.endPoint.y() - self.startPoint.y() self.layer.setCenter(QgsPointXY(x, y)) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showDisplacement(self.startPoint, self.endPoint) def showDisplacement(self, startPoint, endPoint): self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point1 = QgsPointXY(startPoint.x(), startPoint.y()) point2 = QgsPointXY(endPoint.x(), endPoint.y()) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in self.originalCornerPoints: self._addDisplacementToPoint(self.rubberBandExtent, point, False) # for closing self._addDisplacementToPoint(self.rubberBandExtent, self.originalCornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaDisplacement( self.endPoint.x() - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True) self.rasterShadow.show() def _addDisplacementToPoint(self, rubberBand, point, doUpdate): x = point.x() + self.endPoint.x() - self.startPoint.x() y = point.y() + self.endPoint.y() - self.startPoint.y() self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
class InfoTool(QgsMapTool): infoResults = pyqtSignal(dict) def __init__(self, canvas, snapradius = 2): super(InfoTool, self).__init__(canvas) self.canvas = canvas self.radius = snapradius self.band = QgsRubberBand(self.canvas) self.band.setColor(QColor.fromRgb(224,162,16)) self.band.setWidth(3) self.selectband = None self.selectrect = QRect() self.dragging = False self.selectionlayers = [] def getFeatures(self, rect): self.band.reset() for layer in self.selectionlayers: if (not layer.type() == QgsMapLayer.VectorLayer or layer.geometryType() == QGis.NoGeometry): continue rect = self.toLayerCoordinates(layer, rect) rq = QgsFeatureRequest().setFilterRect(rect).setFlags(QgsFeatureRequest.ExactIntersect) features = [] for feature in layer.getFeatures(rq): if feature.isValid(): features.append(feature) yield layer, features def toSearchRect(self, point): searchRadius = self.canvas.extent().width() * ( self.radius / 100.0 ) point = self.toMapCoordinates(point) rect = QgsRectangle() rect.setXMinimum(point.x() - searchRadius) rect.setXMaximum(point.x() + searchRadius) rect.setYMinimum(point.y() - searchRadius) rect.setYMaximum(point.y() + searchRadius) return rect def canvasPressEvent(self, event): self.dragging = False self.selectrect.setRect( 0, 0, 0, 0 ) self.selectband = QgsRubberBand(self.canvas, QGis.Polygon ) self.selectband.setColor(QColor.fromRgb(0,0,255, 65)) self.selectband.setWidth(5) def canvasMoveEvent(self, event): if not event.buttons() == Qt.LeftButton: return if not self.dragging: self.selectrect.setTopLeft(event.pos()) self.dragging = True self.selectrect.setBottomRight(event.pos()) maptoolutils.setRubberBand(self.canvas, self.selectrect, self.selectband) def canvasReleaseEvent(self, event): if self.dragging: geometry = self.selectband.asGeometry() if not geometry: return rect = geometry.boundingBox() else: rect = self.toSearchRect(event.pos()) self.dragging = False self.selectband.reset() results = OrderedDict((l,f) for l, f in self.getFeatures(rect)) print results self.infoResults.emit(results)
class AdjustRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.rubberBandAdjustSide = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.setColor(Qt.red) self.rubberBandAdjustSide.setWidth(3) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): # find the side of the rectangle closest to the click and some data # necessary to compute the new cneter and scale topLeft, topRight, bottomRight, bottomLeft = \ self.layer.cornerCoordinates() top = [topLeft, topRight] right = [bottomRight, topRight] bottom = [bottomRight, bottomLeft] left = [bottomLeft, topLeft] click = QgsGeometry.fromPointXY(self.toMapCoordinates(e.pos())) # order is important (for referenceSide) sides = [top, right, bottom, left] distances = [ click.distance(QgsGeometry.fromPolylineXY(side)) for side in sides ] self.indexSide = self.minDistance(distances) self.side = sides[self.indexSide] self.sidePoint = self.center(self.side) self.vector = self.directionVector(self.side) # side that does not move (opposite of indexSide) self.referenceSide = sides[(self.indexSide + 2) % 4] self.referencePoint = self.center(self.referenceSide) self.referenceDistance = self.distance(self.sidePoint, self.referencePoint) self.isXScale = self.indexSide % 2 == 1 self.startPoint = click.asPoint() self.endPoint = self.startPoint self.isEmittingPoint = True self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) adjustment = self.computeAdjustment() self.showAdjustment(*adjustment) self.layer.history.append({ "action": "adjust", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale }) def minDistance(self, distances): sortedDistances = [ i[0] for i in sorted(enumerate(distances), key=itemgetter(1)) ] # first is min return sortedDistances[0] def directionVector(self, side): sideCenter = self.center(side) layerCenter = self.layer.center vector = [ sideCenter.x() - layerCenter.x(), sideCenter.y() - layerCenter.y() ] norm = math.sqrt(vector[0]**2 + vector[1]**2) normedVector = [vector[0] / norm, vector[1] / norm] return normedVector def center(self, side): return QgsPointXY((side[0].x() + side[1].x()) / 2, (side[0].y() + side[1].y()) / 2) def distance(self, pt1, pt2): return math.sqrt((pt1.x() - pt2.x())**2 + (pt1.y() - pt2.y())**2) def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() center, xScale, yScale = self.computeAdjustment() self.layer.setCenter(center) self.layer.setScale(xScale * self.layer.xScale, yScale * self.layer.yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) adjustment = self.computeAdjustment() self.showAdjustment(*adjustment) def computeAdjustment(self): dX = self.endPoint.x() - self.startPoint.x() dY = self.endPoint.y() - self.startPoint.y() # project on vector dp = dX * self.vector[0] + dY * self.vector[1] # do not go beyond 5% of the current size of side if dp < -0.95 * self.referenceDistance: dp = -0.95 * self.referenceDistance updatedSidePoint = QgsPointXY(self.sidePoint.x() + dp * self.vector[0], self.sidePoint.y() + dp * self.vector[1]) center = self.center([self.referencePoint, updatedSidePoint]) scaleFactor = self.distance(self.referencePoint, updatedSidePoint) if self.isXScale: xScale = scaleFactor / self.referenceDistance yScale = 1.0 else: xScale = 1.0 yScale = scaleFactor / self.referenceDistance return (center, xScale, yScale) def showAdjustment(self, center, xScale, yScale): _, rotation, originalXScale, originalYScale = \ self.layer.transformParameters() newXScale = xScale * originalXScale newYScale = yScale * originalYScale cornerPoints = self.layer.transformedCornerCoordinates( center, rotation, newXScale, newYScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() # show rubberband for side # see def of indexSide in init: # cornerpoints are (topLeft, topRight, bottomRight, bottomLeft) self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry) self.rubberBandAdjustSide.addPoint(cornerPoints[self.indexSide % 4], False) self.rubberBandAdjustSide.addPoint( cornerPoints[(self.indexSide + 1) % 4], True) self.rubberBandAdjustSide.show() self.rasterShadow.reset(self.layer) dx = center.x() - self.layer.center.x() dy = center.y() - self.layer.center.y() self.rasterShadow.setDeltaDisplacement(dx, dy, False) self.rasterShadow.setDeltaScale(xScale, yScale, True) self.rasterShadow.show()
class ProfiletoolMapTool(QgsMapTool): # Signals sig_clearMap = pyqtSignal() sig_createProfile = pyqtSignal() sig_changeCoord = pyqtSignal(QgsPointXY, str) def __init__(self, canvas, drawLineButton, showProfileButton): #buttonShowProf QgsMapTool.__init__(self, canvas) self.canvas = canvas self.cursor = QCursor(Qt.CrossCursor) # Red line self.rubberband = QgsRubberBand(self.canvas) self.rubberband.setWidth(3) self.rubberband.setColor(QColor(231, 28, 35)) # Buttons from main dialog self.drawLineButton = drawLineButton self.buttonShowProf = showProfileButton # Coordinates of drawn line points self.pointsToDraw = [] # Temporary save double clicks self.dblclktemp = None # Drawn line geometry self.drawnLine = None # Point markers on each end of the line self.markers = [] # Backup the last active Tool before the pofile tool became active self.savedTool = self.canvas.mapTool() def drawLine(self): # Emit signal that clears map and deletes profile self.sig_clearMap.emit() self.reset() self.canvas.setMapTool(self) # runs function self.activate() def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): self.canvas.setCursor(QCursor(Qt.OpenHandCursor)) self.pointsToDraw = [] # Stop pressing down button self.drawLineButton.setChecked(False) def reset(self): self.removeStueMarker() self.canvas.setMapTool(self.savedTool) self.rubberband.reset() self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def canvasMoveEvent(self, event): if len(self.pointsToDraw) > 0: self.rubberband.reset() line = [self.pointsToDraw[0], event.mapPoint()] self.rubberband.setToGeometry(QgsGeometry.fromPolylineXY(line), None) def canvasReleaseEvent(self, event): mapPos = event.mapPoint() if mapPos == self.dblclktemp: self.dblclktemp = None return else: # Mark point with marker symbol self.drawStueMarker(mapPos) # Klick ist first point of line if len(self.pointsToDraw) == 0: self.rubberband.reset() self.pointsToDraw.append(mapPos) return # Klick is second point of line elif len(self.pointsToDraw) == 1: self.pointsToDraw.append(mapPos) self.removeStueMarker() self.dblclktemp = mapPos self.drawnLine = self.createDigiFeature(self.pointsToDraw) self.sig_changeCoord.emit(self.pointsToDraw[0], 'A') self.sig_changeCoord.emit(self.pointsToDraw[1], 'E') self.canvas.setMapTool(self.savedTool) # self.deactivate() def setCursor(self, cursor): self.cursor = cursor def updateLine(self, points): self.rubberband.setToGeometry(QgsGeometry.fromPolylineXY(points), None) self.drawnLine = self.createDigiFeature(points) self.drawStueMarker(points[0]) self.drawStueMarker(points[1]) def drawStueMarker(self, point): marker = QgsStueMarker(self.canvas) marker.setCenter(point) self.markers.append(marker) self.canvas.refresh() def removeStueMarker(self, position=-1): if position >= 0: marker = self.markers[position] self.canvas.scene().removeItem(marker) self.markers.pop(position) else: for marker in self.markers: self.canvas.scene().removeItem(marker) self.markers = [] self.canvas.refresh() @staticmethod def createDigiFeature(pnts): line = QgsGeometry.fromPolylineXY(pnts) qgFeat = QgsFeature() qgFeat.setGeometry(line) return qgFeat
class MapWidget(Ui_CanvasWidget, QMainWindow): def __init__(self, parent=None): super(MapWidget, self).__init__(parent) self.setupUi(self) self.snapping = True icon = roam_style.iconsize() self.projecttoolbar.setIconSize(QSize(icon, icon)) self.current_form = None self.last_form = None self.firstshow = True self.layerbuttons = [] self.editfeaturestack = [] self.lastgpsposition = None self.project = None self.gps = None self.gpslogging = None self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas)) self.bridge = QgsLayerTreeMapCanvasBridge( QgsProject.instance().layerTreeRoot(), self.canvas) self.bridge.setAutoSetupOnFirstLayer(False) QgsProject.instance().writeProject.connect(self.bridge.writeProject) QgsProject.instance().readProject.connect(self.bridge.readProject) # self.canvas.setInteractive(False) self.canvas.setCanvasColor(Qt.white) self.canvas.enableAntiAliasing(True) self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor) self.snappingutils = SnappingUtils(self.canvas, self) self.canvas.setSnappingUtils(self.snappingutils) QgsProject.instance().readProject.connect( self.snappingutils.readConfigFromProject) if hasattr(self.canvas, 'setParallelRenderingEnabled'): threadcount = QThread.idealThreadCount() threadcount = 2 if threadcount > 2 else 1 QgsApplication.setMaxThreads(threadcount) self.canvas.setParallelRenderingEnabled(True) pal = QgsPalLabeling() self.canvas.mapRenderer().setLabelingEngine(pal) self.canvas.setFrameStyle(QFrame.NoFrame) self.editgroup = QActionGroup(self) self.editgroup.setExclusive(True) self.editgroup.addAction(self.actionPan) self.editgroup.addAction(self.actionZoom_In) self.editgroup.addAction(self.actionZoom_Out) self.editgroup.addAction(self.actionInfo) self.actionGPS = GPSAction(":/icons/gps", self.canvas, self) self.projecttoolbar.addAction(self.actionGPS) if roam.config.settings.get('north_arrow', False): self.northarrow = NorthArrow(":/icons/north", self.canvas) self.northarrow.setPos(10, 10) self.canvas.scene().addItem(self.northarrow) smallmode = roam.config.settings.get("smallmode", False) self.projecttoolbar.setSmallMode(smallmode) self.scalebar_enabled = roam.config.settings.get('scale_bar', False) if self.scalebar_enabled: self.scalebar = ScaleBarItem(self.canvas) self.canvas.scene().addItem(self.scalebar) self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu) gpsspacewidget = QWidget() gpsspacewidget.setMinimumWidth(30) gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.topspaceraction = self.projecttoolbar.insertWidget( self.actionGPS, gpsspacewidget) self.dataentryselection = QAction(self.projecttoolbar) self.dataentryaction = self.projecttoolbar.insertAction( self.topspaceraction, self.dataentryselection) self.dataentryselection.triggered.connect(self.select_data_entry) self.marker = GPSMarker(self.canvas) self.marker.hide() self.currentfeatureband = CurrentSelection(self.canvas) self.currentfeatureband.setIconSize(30) self.currentfeatureband.setWidth(10) self.currentfeatureband.setColor(QColor(186, 93, 212, 50)) self.currentfeatureband.setOutlineColour(QColor(186, 93, 212)) self.gpsband = QgsRubberBand(self.canvas) self.gpsband.setColor(QColor(165, 111, 212, 75)) self.gpsband.setWidth(5) RoamEvents.editgeometry.connect(self.queue_feature_for_edit) RoamEvents.selectioncleared.connect(self.clear_selection) RoamEvents.selectionchanged.connect(self.highlight_selection) RoamEvents.openfeatureform.connect(self.feature_form_loaded) RoamEvents.sync_complete.connect(self.refresh_map) RoamEvents.snappingChanged.connect(self.snapping_changed) self.snappingbutton = QToolButton() self.snappingbutton.setText("Snapping: On") self.snappingbutton.setAutoRaise(True) self.snappingbutton.pressed.connect(self.toggle_snapping) spacer = QWidget() spacer2 = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.scalewidget = QgsScaleComboBox() self.scalebutton = QToolButton() self.scalebutton.setAutoRaise(True) self.scalebutton.setMaximumHeight(self.statusbar.height()) self.scalebutton.pressed.connect(self.selectscale) self.scalebutton.setText("Scale") self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False) self.scalelist.hide() self.scalelist.setlabel("Map Scale") self.scalelist.setmodel(self.scalewidget.model()) self.scalelist.closewidget.connect(self.scalelist.close) self.scalelist.itemselected.connect(self.update_scale_from_item) self.scalelist.itemselected.connect(self.scalelist.close) self.positionlabel = QLabel('') self.gpslabel = QLabel("GPS: Not active") self.gpslabelposition = QLabel("") self.statusbar.addWidget(self.snappingbutton) self.statusbar.addWidget(spacer2) self.statusbar.addWidget(self.gpslabel) self.statusbar.addWidget(self.gpslabelposition) self.statusbar.addPermanentWidget(self.scalebutton) self.canvas.extentsChanged.connect(self.updatestatuslabel) self.canvas.scaleChanged.connect(self.updatestatuslabel) GPS.gpsposition.connect(self.update_gps_label) GPS.gpsdisconnected.connect(self.gps_disconnected) self.connectButtons() def clear_plugins(self): toolbars = self.findChildren(QToolBar) for toolbar in toolbars: if toolbar.property("plugin_toolbar"): toolbar.unload() self.removeToolBar(toolbar) toolbar.deleteLater() def add_plugins(self, pluginnames): for name in pluginnames: # Get the plugin try: plugin_mod = plugins.loaded_plugins[name] except KeyError: continue if not hasattr(plugin_mod, 'toolbars'): roam.utils.warning( "No toolbars() function found in {}".format(name)) continue toolbars = plugin_mod.toolbars() self.load_plugin_toolbars(toolbars) def load_plugin_toolbars(self, toolbars): for ToolBarClass in toolbars: toolbar = ToolBarClass(plugins.api, self) self.addToolBar(Qt.BottomToolBarArea, toolbar) toolbar.setProperty("plugin_toolbar", True) def snapping_changed(self, snapping): """ Called when the snapping settings have changed. Updates the label in the status bar. :param snapping: """ if snapping: self.snappingbutton.setText("Snapping: On") else: self.snappingbutton.setText("Snapping: Off") def toggle_snapping(self): """ Toggle snapping on or off. """ self.snapping = not self.snapping try: self.canvas.mapTool().toggle_snapping() except AttributeError: pass RoamEvents.snappingChanged.emit(self.snapping) def selectscale(self): """ Show the select scale widget. :return: """ self.scalelist.show() def update_scale_from_item(self, index): """ Update the canvas scale from the selected scale item. :param index: The index of the selected item. """ scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole)) self.canvas.zoomScale(1.0 / scale) def update_gps_label(self, position, gpsinfo): """ Update the GPS label in the status bar with the GPS status. :param position: The current GPS position. :param gpsinfo: The current extra GPS information. """ self.gpslabel.setText( "GPS: PDOP <b>{0:.2f}</b> HDOP <b>{1:.2f}</b> VDOP <b>{2:.2f}</b>" .format(gpsinfo.pdop, gpsinfo.hdop, gpsinfo.vdop)) places = roam.config.settings.get("gpsplaces", 8) self.gpslabelposition.setText( "X <b>{x:.{places}f}</b> Y <b>{y:.{places}f}</b>".format( x=position.x(), y=position.y(), places=places)) def gps_disconnected(self): """ Called when the GPS is disconnected. Updates the label in the status bar with the message. :return: """ self.gpslabel.setText("GPS Not Active") self.gpslabelposition.setText("") def zoom_to_feature(self, feature): box = feature.geometry().boundingBox() xmin, xmax, ymin, ymax = box.xMinimum(), box.xMaximum(), box.yMinimum( ), box.yMaximum() xmin -= 5 xmax += 5 ymin -= 5 ymax += 5 box = QgsRectangle(xmin, ymin, xmax, ymax) self.canvas.setExtent(box) self.canvas.refresh() def updatestatuslabel(self, *args): """ Update the status bar labels when the information has changed. """ extent = self.canvas.extent() self.positionlabel.setText("Map Center: {}".format( extent.center().toString())) scale = 1.0 / self.canvas.scale() scale = self.scalewidget.toString(scale) self.scalebutton.setText(scale) def refresh_map(self): """ Refresh the map """ self.canvas.refresh() def updatescale(self): """ Update the scale of the map with the current scale from the scale widget :return: """ self.canvas.zoomScale(1.0 / self.scalewidget.scale()) def init_qgisproject(self, doc): """ Called when the project file is read for the firs time. :param doc: The XML doc. :return: The current canvas CRS :note: This method is old and needs to be refactored into something else. """ return self.canvas.mapSettings().destinationCrs() def showEvent(self, *args, **kwargs): """ Handle the show event of the of the map widget. We have to do a little hack here to make the QGIS map refresh. """ if QGis.QGIS_VERSION_INT == 20200 and self.firstshow: self.canvas.refresh() self.canvas.repaint() self.firstshow = False def feature_form_loaded(self, form, feature, *args): """ Called when the feature form is loaded. :param form: The Form object. Holds a reference to the forms layer. :param feature: The current capture feature """ self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer) def highlight_selection(self, results): """ Highlight the selection on the canvas. This updates all selected objects based on the result set. :param results: A dict-of-list of layer-features. """ self.clear_selection() for layer, features in results.iteritems(): band = self.selectionbands[layer] band.setColor(QColor(255, 0, 0)) band.setIconSize(25) band.setWidth(5) band.setBrushStyle(Qt.NoBrush) band.reset(layer.geometryType()) band.setZValue(self.currentfeatureband.zValue() - 1) for feature in features: band.addGeometry(feature.geometry(), layer) self.canvas.update() def highlight_active_selection(self, layer, feature, features): """ Update the current active selected feature. :param layer: The layer of the active feature. :param feature: The active feature. :param features: The other features in the set to show as non active selection. :return: """ self.clear_selection() self.highlight_selection({layer: features}) self.currentfeatureband.setToGeometry(feature.geometry(), layer) self.canvas.update() def clear_selection(self): """ Clear the selection from the canvas. Resets all selection rubbber bands. :return: """ # Clear the main selection rubber band self.canvas.scene().update() self.currentfeatureband.reset() # Clear the rest for band in self.selectionbands.itervalues(): band.reset() self.canvas.update() self.editfeaturestack = [] def queue_feature_for_edit(self, form, feature): """ Push a feature on the edit stack so the feature can have the geometry edited. :note: This is a big hack and I don't like it! :param form: The form for the current feature :param feature: The active feature. """ def trigger_default_action(): for action in self.projecttoolbar.actions(): if action.property('dataentry') and action.isdefault: action.trigger() self.canvas.mapTool().setEditMode(True, feature.geometry()) break self.editfeaturestack.append((form, feature)) self.save_current_form() self.load_form(form) trigger_default_action() def save_current_form(self): self.last_form = self.current_form def restore_last_form(self): self.load_form(self.last_form) def clear_temp_objects(self): """ Clear all temp objects from the canvas. :return: """ def clear_tool_band(): """ Clear the rubber band of the active tool if it has one """ tool = self.canvas.mapTool() try: tool.clearBand() except AttributeError: # No clearBand method found, but that's cool. pass self.currentfeatureband.reset() clear_tool_band() def settings_updated(self, settings): """ Called when the settings have been updated in the Roam config. :param settings: A dict of the settings. """ self.actionGPS.updateGPSPort() gpslogging = settings.get('gpslogging', True) if self.gpslogging: self.gpslogging.logging = gpslogging def set_gps(self, gps, logging): """ Set the GPS for the map widget. Connects GPS signals """ self.gps = gps self.gpslogging = logging self.gps.gpsposition.connect(self.gps_update_canvas) self.gps.firstfix.connect(self.gps_first_fix) self.gps.gpsdisconnected.connect(self.gps_disconnected) def gps_update_canvas(self, position, gpsinfo): """ Updates the map canvas based on the GPS position. By default if the GPS is outside the canvas extent the canvas will move to center on the GPS. Can be turned off in settings. :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ # Recenter map if we go outside of the 95% of the area if self.gpslogging.logging: self.gpsband.addPoint(position) self.gpsband.show() if roam.config.settings.get('gpscenter', True): if not self.lastgpsposition == position: self.lastposition = position rect = QgsRectangle(position, position) extentlimt = QgsRectangle(self.canvas.extent()) extentlimt.scale(0.95) if not extentlimt.contains(position): self.zoom_to_location(position) self.marker.show() self.marker.setCenter(position, gpsinfo) def gps_first_fix(self, postion, gpsinfo): """ Called the first time the GPS gets a fix. If set this will zoom to the GPS after the first fix :param postion: The current GPS position. :param gpsinfo: The extra GPS information """ zoomtolocation = roam.config.settings.get('gpszoomonfix', True) if zoomtolocation: self.canvas.zoomScale(1000) self.zoom_to_location(postion) def zoom_to_location(self, position): """ Zoom to ta given position on the map.. """ rect = QgsRectangle(position, position) self.canvas.setExtent(rect) self.canvas.refresh() def gps_disconnected(self): """ Called when the GPS is disconnected """ self.marker.hide() def select_data_entry(self): """ Open the form selection widget to allow the user to pick the active capture form. """ def showformerror(form): pass def actions(): for form in self.project.forms: if not self.form_valid_for_capture(form): continue action = form.createuiaction() valid, failreasons = form.valid if not valid: roam.utils.warning("Form {} failed to load".format( form.label)) roam.utils.warning("Reasons {}".format(failreasons)) action.triggered.connect(partial(showformerror, form)) else: action.triggered.connect(partial(self.load_form, form)) yield action formpicker = PickActionDialog(msg="Select data entry form", wrap=5) formpicker.addactions(actions()) formpicker.exec_() def project_loaded(self, project): """ Called when the project is loaded. Main entry point for a loade project. :param project: The Roam project that has been loaded. """ self.project = project self.actionPan.trigger() firstform = self.first_capture_form() if firstform: self.load_form(firstform) self.dataentryselection.setVisible(True) else: self.dataentryselection.setVisible(False) # Enable the raster layers button only if the project contains a raster layer. layers = QgsMapLayerRegistry.instance().mapLayers().values() hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers) self.actionRaster.setEnabled(hasrasters) self.defaultextent = self.canvas.extent() roam.utils.info("Extent: {}".format(self.defaultextent.toString())) self.infoTool.selectionlayers = project.selectlayersmapping() self.canvas.refresh() projectscales, _ = QgsProject.instance().readBoolEntry( "Scales", "/useProjectScales") if projectscales: projectscales, _ = QgsProject.instance().readListEntry( "Scales", "/ScalesList") self.scalewidget.updateScales(projectscales) else: scales = [ "1:50000", "1:25000", "1:10000", "1:5000", "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100" ] scales = roam.config.settings.get('scales', scales) self.scalewidget.updateScales(scales) if self.scalebar_enabled: self.scalebar.update() self.actionPan.toggle() self.clear_plugins() self.add_plugins(project.enabled_plugins) def setMapTool(self, tool, *args): """ Set the active map tool in the canvas. :param tool: The QgsMapTool to set. """ if tool == self.canvas.mapTool(): return if hasattr(tool, "setSnapping"): tool.setSnapping(self.snapping) self.canvas.setMapTool(tool) def connectButtons(self): """ Connect the default buttons in the interface. Zoom, pan, etc """ def connectAction(action, tool): action.toggled.connect(partial(self.setMapTool, tool)) def cursor(name): pix = QPixmap(name) pix = pix.scaled(QSize(24, 24)) return QCursor(pix) self.zoomInTool = QgsMapToolZoom(self.canvas, False) self.zoomOutTool = QgsMapToolZoom(self.canvas, True) self.panTool = PanTool(self.canvas) self.infoTool = InfoTool(self.canvas) self.infoTool.setAction(self.actionInfo) self.zoomInTool.setAction(self.actionZoom_In) self.zoomOutTool.setAction(self.actionZoom_Out) self.panTool.setAction(self.actionPan) connectAction(self.actionZoom_In, self.zoomInTool) connectAction(self.actionZoom_Out, self.zoomOutTool) connectAction(self.actionPan, self.panTool) connectAction(self.actionInfo, self.infoTool) self.zoomInTool.setCursor(cursor(':/icons/in')) self.zoomOutTool.setCursor(cursor(':/icons/out')) self.infoTool.setCursor(cursor(':/icons/select')) self.actionRaster.triggered.connect(self.toggleRasterLayers) self.actionHome.triggered.connect(self.homeview) def homeview(self): """ Zoom the mapview canvas to the extents the project was opened at i.e. the default extent. """ self.canvas.setExtent(self.defaultextent) self.canvas.refresh() def form_valid_for_capture(self, form): """ Check if the given form is valid for capture. :param form: The form to check. :return: True if valid form for capture """ return form.has_geometry and self.project.layer_can_capture( form.QGISLayer) def first_capture_form(self): """ Return the first valid form for capture. """ for form in self.project.forms: if self.form_valid_for_capture(form): return form def load_form(self, form): """ Load the given form so it's the active one for capture :param form: The form to load """ self.clearCaptureTools() self.dataentryselection.setIcon(QIcon(form.icon)) self.dataentryselection.setText(form.icontext) self.create_capture_buttons(form) self.current_form = form def create_capture_buttons(self, form): """ Create the capture buttons in the toolbar for the given form. :param form: The active form. """ layer = form.QGISLayer tool = form.getMaptool()(self.canvas, form.settings) for action in tool.actions: # Create the action here. if action.ismaptool: action.toggled.connect(partial(self.setMapTool, tool)) # Set the action as a data entry button so we can remove it later. action.setProperty("dataentry", True) self.editgroup.addAction(action) self.layerbuttons.append(action) self.projecttoolbar.insertAction(self.topspaceraction, action) action.setChecked(action.isdefault) if hasattr(tool, 'geometryComplete'): add = partial(self.add_new_feature, form) tool.geometryComplete.connect(add) else: tool.finished.connect(self.openForm) tool.error.connect(self.show_invalid_geometry_message) def show_invalid_geometry_message(self, message): RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL) def add_new_feature(self, form, geometry): """ Add a new new feature to the given layer """ # TODO Extract into function. # NOTE This function is doing too much, acts as add and also edit. layer = form.QGISLayer if layer.geometryType() in [ QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon ]: geometry.convertToMultiType() try: form, feature = self.editfeaturestack.pop() self.editfeaturegeometry(form, feature, newgeometry=geometry) return except IndexError: pass feature = form.new_feature(geometry=geometry) RoamEvents.load_feature_form(form, feature, editmode=False) def editfeaturegeometry(self, form, feature, newgeometry): # TODO Extract into function. layer = form.QGISLayer layer.startEditing() feature.setGeometry(newgeometry) layer.updateFeature(feature) saved = layer.commitChanges() if not saved: map(roam.utils.error, layer.commitErrors()) self.canvas.refresh() self.currentfeatureband.setToGeometry(feature.geometry(), layer) RoamEvents.editgeometry_complete.emit(form, feature) self.canvas.mapTool().setEditMode(False, None) self.restore_last_form() def clearCaptureTools(self): """ Clear the capture tools from the toolbar. :return: True if the capture button was active at the time of clearing. """ captureselected = False for action in self.projecttoolbar.actions(): if action.objectName() == "capture" and action.isChecked(): captureselected = True if action.property('dataentry'): self.projecttoolbar.removeAction(action) return captureselected def toggleRasterLayers(self): """ Toggle all raster layers on or off. """ # Freeze the canvas to save on UI refresh self.canvas.freeze() tree = QgsProject.instance().layerTreeRoot() for node in tree.findLayers(): if node.layer().type() == QgsMapLayer.RasterLayer: if node.isVisible() == Qt.Checked: state = Qt.Unchecked else: state = Qt.Checked node.setVisible(state) self.canvas.freeze(False) self.canvas.refresh() def cleanup(self): """ Clean up when the project has changed. :return: """ self.bridge.clear() self.gpsband.reset() self.gpsband.hide() self.clear_selection() self.clear_temp_objects() self.clearCaptureTools() self.canvas.freeze() self.canvas.clear() self.canvas.freeze(False) for action in self.layerbuttons: self.editgroup.removeAction(action)
class ObstacleAreaJigCreateArea(QgsMapTool): def __init__(self, canvas, areaType): QgsMapTool.__init__(self, canvas) self.mCanvas = canvas self.areaType = areaType self.annotation = None self.rubberBand = QgsRubberBand(canvas, QGis.Point) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(10) self.rubberBandClick = QgsRubberBand(canvas, QGis.Point) self.rubberBandClick.setColor(Qt.green) self.rubberBandClick.setWidth(3) self.obstaclesLayerList = QgisHelper.getSurfaceLayers( SurfaceTypes.Obstacles) self.demLayerList = QgisHelper.getSurfaceLayers(SurfaceTypes.DEM) self.mRubberBand = None self.mRubberBand0 = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mCursor = Qt.ArrowCursor self.mFillColor = QColor(254, 178, 76, 63) self.mBorderColour = QColor(254, 58, 29, 100) self.mRubberBand0.setBorderColor(self.mBorderColour) self.polygonGeom = None self.drawFlag = False self.mSnapper = QgsMapCanvasSnapper(canvas) self.resultPolylineArea = PolylineArea() # self.constructionLayer = constructionLayer self.menuString = "" self.isPrimaryPolylineStarted = False self.primaryPolyline = PolylineArea() def createContextMenu(self, areaType, isStarted=False): menu = QMenu() # h = QHBoxLayout(menu) # c = QCalendarWidget() # h.addWidget(c) if areaType == ProtectionAreaType.Primary: actionEnter = QgisHelper.createAction(menu, "Enter", self.menuEnterClick) actionCancel = QgisHelper.createAction(menu, "Cancel", self.menuCancelClick) actionArc = QgisHelper.createAction(menu, "Arc", self.menuArcClick) actionUndo = QgisHelper.createAction(menu, "Undo", self.menuUndoClick) menu.addAction(actionEnter) menu.addAction(actionCancel) menu.addAction(actionArc) menu.addAction(actionUndo) elif areaType == ProtectionAreaType.Secondary: if not isStarted: actionEnter = QgisHelper.createAction(menu, "Enter", self.menuEnterClick) actionCancel = QgisHelper.createAction(menu, "Cancel", self.menuCancelClick) actionUndo = QgisHelper.createAction(menu, "Undo", self.menuUndoClick) actionPrimatyPolylineStart = QgisHelper.createAction( menu, "Strat INNER edge of the secondary area", self.menuPrimaryStartClick) actionPrimatyPolylineEnd = QgisHelper.createAction( menu, "End INNER edge of the secondary area", self.menuPrimaryEndClick) menu.addAction(actionEnter) menu.addAction(actionCancel) menu.addAction(actionUndo) menu.addAction(actionPrimatyPolylineStart) menu.addAction(actionPrimatyPolylineEnd) actionPrimatyPolylineStart.setEnabled( not self.isPrimaryPolylineStarted) actionPrimatyPolylineEnd.setEnabled( self.isPrimaryPolylineStarted) else: actionPrimatyPolylineStart = QgisHelper.createAction( menu, "Strat INNER edge of the secondary area", self.menuPrimaryStartClick) actionPrimatyPolylineEnd = QgisHelper.createAction( menu, "End INNER edge of the secondary area", self.menuPrimaryEndClick) menu.addAction(actionPrimatyPolylineStart) menu.addAction(actionPrimatyPolylineEnd) actionPrimatyPolylineStart.setEnabled( not self.isPrimaryPolylineStarted) actionPrimatyPolylineEnd.setEnabled( self.isPrimaryPolylineStarted) return menu def menuPrimaryStartClick(self): self.primaryPolyline = PolylineArea() self.isPrimaryPolylineStarted = True # self.menuString = "Enter" def menuPrimaryEndClick(self): self.isPrimaryPolylineStarted = False # self.menuString = "Enter" def menuEnterClick(self): self.menuString = "Enter" def menuCancelClick(self): self.menuString = "Cancel" def menuArcClick(self): self.menuString = "Arc" def menuUndoClick(self): self.menuString = "Undo" def reset(self): self.Point = None def canvasPressEvent(self, e): define._messageLabel.setText("") self.menuString = "" pointBackground = e.pos() # self.Point = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas) self.Point, self.pointID, self.layer = self.snapPoint(e.pos()) self.selectedLayerFromSnapPoint = None if (self.mRubberBand == None): self.resultPolylineArea = PolylineArea() self.mRubberBand0.reset(QGis.Polygon) # define._canvas.clearCache () self.mRubberBand = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mRubberBand0 = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mRubberBand.setFillColor(self.mFillColor) self.mRubberBand.setBorderColor(self.mBorderColour) self.mRubberBand0.setFillColor(QColor(255, 255, 255, 100)) self.mRubberBand0.setBorderColor(QColor(0, 0, 0)) if (e.button() == Qt.LeftButton): if self.Point == None: self.mRubberBand.addPoint(self.toMapCoordinates(e.pos())) self.resultPolylineArea.Add( PolylineAreaPoint(self.toMapCoordinates(e.pos()))) if self.isPrimaryPolylineStarted: self.primaryPolyline.Add( PolylineAreaPoint(self.toMapCoordinates(e.pos()))) else: self.mRubberBand.addPoint(self.Point) self.resultPolylineArea.Add(PolylineAreaPoint(self.Point)) if self.isPrimaryPolylineStarted: self.primaryPolyline.Add( PolylineAreaPoint(self.toMapCoordinates(e.pos()))) else: menu = None if self.areaType == ProtectionAreaType.Secondary and len( self.resultPolylineArea) == 0: menu = self.createContextMenu(self.areaType, True) menu.exec_(define._canvas.mapToGlobal(e.pos())) return if (self.mRubberBand.numberOfVertices() > 2): self.polygonGeom = self.mRubberBand.asGeometry() else: return # QgsMapToolSelectUtils.setSelectFeatures( self.mCanvas, polygonGeom, e ) menu = self.createContextMenu(self.areaType) menu.exec_(define._canvas.mapToGlobal(e.pos())) if self.menuString == "Cancel" or self.menuString == "Arc": return elif self.menuString == "Undo": if (self.mRubberBand.numberOfVertices() > 0): self.mRubberBand = None QgisHelper.ClearRubberBandInCanvas(define._canvas) self.mRubberBand = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mRubberBand.setFillColor(self.mFillColor) self.mRubberBand.setBorderColor(self.mBorderColour) self.resultPolylineArea[self.resultPolylineArea.Count - 2].bulge = 0.0 self.resultPolylineArea.pop(self.resultPolylineArea.Count - 1) if self.isPrimaryPolylineStarted and len( self.primaryPolyline) > 0: self.primaryPolyline.pop(self.primaryPolyline.Count - 1) for pt in self.resultPolylineArea.method_14(): self.mRubberBand.addPoint(pt) return elif self.menuString == "Enter": # if self.areaType == ProtectionAreaType.Secondary: # if self.resultPolylineArea.Count != 4: # define._messageLabel.setText("The count of point of Secondary Area must be 4.") # return self.mRubberBand.reset(QGis.Polygon) self.mRubberBand0.reset(QGis.Polygon) # define._canvas.clearCache () self.mRubberBand0 = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mRubberBand0.setFillColor(QColor(255, 255, 255, 100)) self.mRubberBand0.setBorderColor(QColor(0, 0, 0)) for pt in self.resultPolylineArea.method_14(): self.mRubberBand0.addPoint(pt) # self.mRubberBand0.addGeometry(self.polygonGeom, None) n = self.mRubberBand0.numberOfVertices() self.mRubberBand0.show() self.mRubberBand = None area = None if self.areaType == ProtectionAreaType.Primary: area = PrimaryObstacleArea(self.resultPolylineArea) elif self.areaType == ProtectionAreaType.Secondary: if len(self.resultPolylineArea) == 4: area = SecondaryObstacleArea( self.resultPolylineArea[0].Position, self.resultPolylineArea[1].Position, self.resultPolylineArea[3].Position, self.resultPolylineArea[2].Position, MathHelper.getBearing( self.resultPolylineArea[0].Position, self.resultPolylineArea[1].Position)) else: if self.primaryPolyline.Count < 2: define._messageLabel.setText( "The PrimaryLine in Secondary Area must exist." ) return if self.isPrimaryPolylineStarted: define._messageLabel.setText( "You must finish the input of PrimaryLine.") return area = SecondaryObstacleAreaWithManyPoints( self.resultPolylineArea, self.primaryPolyline) self.emit(SIGNAL("outputResult"), area, self.mRubberBand0) n = 0 def canvasMoveEvent(self, e): self.rubberBand.reset(QGis.Point) # snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper , define._canvas, True) snapPoint, snapPointID, layer = self.snapPoint(e.pos(), True) if snapPoint != None: self.rubberBand.addPoint(snapPoint) self.rubberBand.show() if (self.mRubberBand == None): return if (self.mRubberBand.numberOfVertices() > 0): if self.menuString != "Undo": self.mRubberBand.removeLastPoint(0) else: self.menuString = "" point2 = None if snapPoint != None: self.mRubberBand.addPoint(snapPoint) point2 = snapPoint else: self.mRubberBand.addPoint(self.toMapCoordinates(e.pos())) point2 = self.toMapCoordinates(e.pos()) if self.menuString == "Arc": point0 = self.resultPolylineArea[self.resultPolylineArea.Count - 2].Position point1 = self.resultPolylineArea[self.resultPolylineArea.Count - 1].Position # point2 = self.mRubberBand.getPoint(self.mRubberBand.numberOfVertices() - 1) bulge = MathHelper.smethod_60(point0, point1, point2) self.resultPolylineArea[self.resultPolylineArea.Count - 2].bulge = bulge self.mRubberBand = None QgisHelper.ClearRubberBandInCanvas(define._canvas) self.mRubberBand = QgsRubberBand(self.mCanvas, QGis.Polygon) self.mRubberBand.setFillColor(self.mFillColor) self.mRubberBand.setBorderColor(self.mBorderColour) for pt in self.resultPolylineArea.method_14(): self.mRubberBand.addPoint(pt) self.mRubberBand.addPoint(point2) def snapPoint(self, p, bNone=False): if define._snapping == False: return ( define._canvas.getCoordinateTransform().toMapCoordinates(p), None, None) snappingResults = self.mSnapper.snapToBackgroundLayers(p) if (snappingResults[0] != 0 or len(snappingResults[1]) < 1): if bNone: return (None, None, None) else: return (define._canvas.getCoordinateTransform(). toMapCoordinates(p), None, None) else: return (snappingResults[1][0].snappedVertex, snappingResults[1][0].snappedAtGeometry, snappingResults[1][0].layer) def deactivate(self): self.rubberBand.reset(QGis.Point) QgsMapTool.deactivate(self) self.emit(SIGNAL("deactivated()"))
class ScaleRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): pressed_button = e.button() if pressed_button == 1: self.startPoint = e.pos() self.endPoint = self.startPoint self.isEmittingPoint = True self.height = float(self.canvas.height()) self.width = float(self.canvas.width()) modifiers = QApplication.keyboardModifiers() self.isKeepRelativeScale = bool(modifiers & Qt.ControlModifier) self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) scaling = self.computeScaling() self.showScaling(*scaling) self.layer.history.append({ "action": "scale", "xScale": self.layer.xScale, "yScale": self.layer.yScale }) def canvasReleaseEvent(self, e): pressed_button = e.button() if pressed_button == 1: self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() xScale, yScale = self.computeScaling() self.layer.setScale(xScale * self.layer.xScale, yScale * self.layer.yScale) setLayerVisible(self.iface, self.layer, self.isLayerVisible) elif pressed_button == 2: number, ok = QInputDialog.getText( None, "Scale & DPI", "Enter scale,dpi (e.g. 3000,96)") if not ok: self.layer.history.pop() return scales = number.split(',') if len(scales) != 2: self.layer.history.pop() QMessageBox.information(self.iface.mainWindow(), "Error", "Must be 2 numbers") return scale = tryfloat(scales[0]) dpi = tryfloat(scales[1]) if scale and dpi: xScale = scale / (dpi / 0.0254) yScale = xScale else: self.layer.history.pop() QMessageBox.information( self.iface.mainWindow(), "Error", "Bad format: Must be scale,dpi (e.g. 3000,96)") return self.layer.setScale(xScale, yScale) self.layer.repaint() self.layer.commitTransformParameters() def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = e.pos() scaling = self.computeScaling() self.showScaling(*scaling) def computeScaling(self): dX = -(self.endPoint.x() - self.startPoint.x()) dY = self.endPoint.y() - self.startPoint.y() xScale = 1.0 - (dX / (self.width * 1.1)) yScale = 1.0 - (dY / (self.height * 1.1)) if self.isKeepRelativeScale: # keep same scale in both dimensions return (xScale, xScale) else: return (xScale, yScale) def showScaling(self, xScale, yScale): if xScale == 0 and yScale == 0: return center, rotation, originalXScale, originalYScale = \ self.layer.transformParameters() newXScale = xScale * originalXScale newYScale = yScale * originalYScale cornerPoints = self.layer.transformedCornerCoordinates( center, rotation, newXScale, newYScale) self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show() self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaScale(xScale, yScale, True) self.rasterShadow.show()
class RectangleMapTool(QgsMapToolEmitPoint): """ Map tool that lets the user define the analysis extents. """ rectangle_created = pyqtSignal() deactivated = pyqtSignal() def __init__(self, canvas): """Constructor for the map tool. :param canvas: Canvas that tool will interact with. :type canvas: QgsMapCanvas """ self.canvas = canvas self.start_point = None self.end_point = None self.is_emitting_point = False QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubber_band = QgsRubberBand(self.canvas, geometryType=QGis.Line) self.rubber_band.setColor(QColor(0, 0, 240, 100)) # Needs QGIS 2.6 # self.rubber_band.setFillColor(QColor(0, 0, 240, 0)) self.rubber_band.setWidth(1) self.reset() def reset(self): """ Clear the rubber band for the analysis extents. """ self.start_point = self.end_point = None self.is_emitting_point = False self.rubber_band.reset(QGis.Polygon) def canvasPressEvent(self, e): """ Handle canvas press events so we know when user is capturing the rect. :param e: A Qt event object. :type: QEvent """ self.start_point = self.toMapCoordinates(e.pos()) self.end_point = self.start_point self.is_emitting_point = True self.show_rectangle(self.start_point, self.end_point) def canvasReleaseEvent(self, e): """ Handle canvas release events has finished capturing e :param e: A Qt event object. :type: QEvent """ _ = e self.is_emitting_point = False self.rectangle_created.emit() def canvasMoveEvent(self, e): """ :param e: :return: """ if not self.is_emitting_point: return self.end_point = self.toMapCoordinates(e.pos()) self.show_rectangle(self.start_point, self.end_point) def show_rectangle(self, start_point, end_point): """ Show the rectangle on the canvas. :param start_point: QGIS Point object representing the origin ( top left). :type start_point: QgsPoint :param end_point: QGIS Point object representing the contra-origin ( bottom right). :type end_point: QgsPoint :return: """ self.rubber_band.reset(QGis.Polygon) if (start_point.x() == end_point.x() or start_point.y() == end_point.y()): return point1 = start_point point2 = QgsPoint(end_point.x(), start_point.y()) point3 = end_point point4 = QgsPoint(start_point.x(), end_point.y()) update_canvas = False self.rubber_band.addPoint(point1, update_canvas) self.rubber_band.addPoint(point2, update_canvas) self.rubber_band.addPoint(point3, update_canvas) self.rubber_band.addPoint(point4, update_canvas) # noinspection PyArgumentEqualDefault # no False so canvas will update # close the polygon otherwise it shows as a filled rect self.rubber_band.addPoint(point1) self.rubber_band.show() def rectangle(self): """ Accessor for the rectangle. :return: A rectangle showing the designed extent. :rtype: QgsRectangle """ if self.start_point is None or self.end_point is None: return None elif self.start_point.x() == self.end_point.x() or \ self.start_point.y() == self.end_point.y(): return None return QgsRectangle(self.start_point, self.end_point) def set_rectangle(self, rectangle): """ Set the rectangle for the selection. :param rectangle: :return: """ if rectangle == self.rectangle(): return False if rectangle is None: self.reset() else: self.start_point = QgsPoint( rectangle.xMinimum(), rectangle.yMinimum()) self.end_point = QgsPoint( rectangle.xMaximum(), rectangle.yMaximum()) self.show_rectangle(self.start_point, self.end_point) return True def deactivate(self): """ Disable the tool. """ QgsMapTool.deactivate(self) self.deactivated.emit()
class LatLonTools: digitizerDialog = None def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() self.crossRb = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.crossRb.setColor(Qt.red) self.provider = LatLonToolsProvider() self.toolbar = self.iface.addToolBar('Lat Lon Tools Toolbar') self.toolbar.setObjectName('LatLonToolsToolbar') def initGui(self): '''Initialize Lot Lon Tools GUI.''' # Initialize the Settings Dialog box self.settingsDialog = SettingsWidget(self, self.iface, self.iface.mainWindow()) self.mapTool = CopyLatLonTool(self.settingsDialog, self.iface) self.showMapTool = ShowOnMapTool(self.iface) # Add Interface for Coordinate Capturing icon = QIcon(os.path.dirname(__file__) + "/images/copyicon.png") self.copyAction = QAction(icon, "Copy Latitude, Longitude", self.iface.mainWindow()) self.copyAction.setObjectName('latLonToolsCopy') self.copyAction.triggered.connect(self.startCapture) self.copyAction.setCheckable(True) self.toolbar.addAction(self.copyAction) self.iface.addPluginToMenu("Lat Lon Tools", self.copyAction) # Add Interface for External Map icon = QIcon(os.path.dirname(__file__) + "/images/mapicon.png") self.externMapAction = QAction(icon, "Show in External Map", self.iface.mainWindow()) self.externMapAction.setObjectName('latLonToolsExternalMap') self.externMapAction.triggered.connect(self.setShowMapTool) self.externMapAction.setCheckable(True) self.toolbar.addAction(self.externMapAction) self.iface.addPluginToMenu("Lat Lon Tools", self.externMapAction) # Add Interface for Zoom to Coordinate icon = QIcon(os.path.dirname(__file__) + "/images/zoomicon.png") self.zoomToAction = QAction(icon, "Zoom To Latitude, Longitude", self.iface.mainWindow()) self.zoomToAction.setObjectName('latLonToolsZoom') self.zoomToAction.triggered.connect(self.showZoomToDialog) self.toolbar.addAction(self.zoomToAction) self.iface.addPluginToMenu('Lat Lon Tools', self.zoomToAction) self.zoomToDialog = ZoomToLatLon(self, self.iface, self.iface.mainWindow()) self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.zoomToDialog) self.zoomToDialog.hide() # Add Interface for Multi point zoom icon = QIcon(os.path.dirname(__file__) + '/images/multizoom.png') self.multiZoomToAction = QAction(icon, "Multi-location Zoom", self.iface.mainWindow()) self.multiZoomToAction.setObjectName('latLonToolsMultiZoom') self.multiZoomToAction.triggered.connect(self.multiZoomTo) self.toolbar.addAction(self.multiZoomToAction) self.iface.addPluginToMenu('Lat Lon Tools', self.multiZoomToAction) self.multiZoomDialog = MultiZoomWidget(self, self.settingsDialog, self.iface.mainWindow()) self.multiZoomDialog.hide() self.multiZoomDialog.setFloating(True) # Create the conversions menu menu = QMenu() icon = QIcon(os.path.dirname(__file__) + '/images/field2geom.png') action = menu.addAction(icon, "Fields to point layer", self.field2geom) action.setObjectName('latLonToolsField2Geom') icon = QIcon(os.path.dirname(__file__) + '/images/geom2field.png') action = menu.addAction(icon, "Point layer to fields", self.geom2Field) action.setObjectName('latLonToolsGeom2Field') icon = QIcon(os.path.dirname(__file__) + '/images/pluscodes.png') action = menu.addAction(icon, "Plus Codes to point layer", self.PlusCodestoLayer) action.setObjectName('latLonToolsPlusCodes2Geom') action = menu.addAction(icon, "Point layer to Plus Codes", self.toPlusCodes) action.setObjectName('latLonToolsGeom2PlusCodes') icon = QIcon(os.path.dirname(__file__) + '/images/mgrs2point.png') action = menu.addAction(icon, "MGRS to point layer", self.MGRStoLayer) action.setObjectName('latLonToolsMGRS2Geom') icon = QIcon(os.path.dirname(__file__) + '/images/point2mgrs.png') action = menu.addAction(icon, "Point layer to MGRS", self.toMGRS) action.setObjectName('latLonToolsGeom2MGRS') self.conversionsAction = QAction(icon, "Conversions", self.iface.mainWindow()) self.conversionsAction.setMenu(menu) self.iface.addPluginToMenu('Lat Lon Tools', self.conversionsAction) # Add to Digitize Toolbar icon = QIcon(os.path.dirname(__file__) + '/images/latLonDigitize.png') self.digitizeAction = QAction(icon, "Lat Lon Digitize", self.iface.mainWindow()) self.digitizeAction.setObjectName('latLonToolsDigitize') self.digitizeAction.triggered.connect(self.digitizeClicked) self.digitizeAction.setEnabled(False) self.toolbar.addAction(self.digitizeAction) self.iface.addPluginToMenu('Lat Lon Tools', self.digitizeAction) # Add Interface for copying the canvas extent icon = QIcon(os.path.dirname(__file__) + "/images/copycanvas.png") self.copyCanvasAction = QAction(icon, "Copy Canvas Bounding Box", self.iface.mainWindow()) self.copyCanvasAction.setObjectName('latLonToolsCopyCanvas') self.copyCanvasAction.triggered.connect(self.copyCanvas) self.toolbar.addAction(self.copyCanvasAction) self.iface.addPluginToMenu("Lat Lon Tools", self.copyCanvasAction) # Initialize the Settings Dialog Box settingsicon = QIcon( os.path.dirname(__file__) + '/images/settings.png') self.settingsAction = QAction(settingsicon, "Settings", self.iface.mainWindow()) self.settingsAction.setObjectName('latLonToolsSettings') self.settingsAction.triggered.connect(self.settings) self.iface.addPluginToMenu('Lat Lon Tools', self.settingsAction) # Help icon = QIcon(os.path.dirname(__file__) + '/images/help.png') self.helpAction = QAction(icon, "Help", self.iface.mainWindow()) self.helpAction.setObjectName('latLonToolsHelp') self.helpAction.triggered.connect(self.help) self.iface.addPluginToMenu('Lat Lon Tools', self.helpAction) self.iface.currentLayerChanged.connect(self.currentLayerChanged) self.canvas.mapToolSet.connect(self.unsetTool) self.enableDigitizeTool() # Add the processing provider QgsApplication.processingRegistry().addProvider(self.provider) def unsetTool(self, tool): '''Uncheck the Copy Lat Lon tool''' try: if not isinstance(tool, CopyLatLonTool): self.copyAction.setChecked(False) self.multiZoomDialog.stopCapture() self.mapTool.capture4326 = False if not isinstance(tool, ShowOnMapTool): self.externMapAction.setChecked(False) except: pass def unload(self): '''Unload LatLonTools from the QGIS interface''' self.zoomToDialog.removeMarker() self.multiZoomDialog.removeMarkers() self.canvas.unsetMapTool(self.mapTool) self.canvas.unsetMapTool(self.showMapTool) self.iface.removePluginMenu('Lat Lon Tools', self.copyAction) self.iface.removePluginMenu('Lat Lon Tools', self.copyCanvasAction) self.iface.removePluginMenu('Lat Lon Tools', self.externMapAction) self.iface.removePluginMenu('Lat Lon Tools', self.zoomToAction) self.iface.removePluginMenu('Lat Lon Tools', self.multiZoomToAction) self.iface.removePluginMenu('Lat Lon Tools', self.conversionsAction) self.iface.removePluginMenu('Lat Lon Tools', self.settingsAction) self.iface.removePluginMenu('Lat Lon Tools', self.helpAction) self.iface.removePluginMenu('Lat Lon Tools', self.digitizeAction) self.iface.removeDockWidget(self.zoomToDialog) self.iface.removeDockWidget(self.multiZoomDialog) # Remove Toolbar Icons self.iface.removeToolBarIcon(self.copyAction) self.iface.removeToolBarIcon(self.copyCanvasAction) self.iface.removeToolBarIcon(self.zoomToAction) self.iface.removeToolBarIcon(self.externMapAction) self.iface.removeToolBarIcon(self.multiZoomToAction) self.iface.removeToolBarIcon(self.digitizeAction) del self.toolbar self.zoomToDialog = None self.multiZoomDialog = None self.settingsDialog = None self.showMapTool = None self.mapTool = None self.digitizerDialog = None QgsApplication.processingRegistry().removeProvider(self.provider) def startCapture(self): '''Set the focus of the copy coordinate tool and check it''' self.copyAction.setChecked(True) self.canvas.setMapTool(self.mapTool) def copyCanvas(self): extent = self.iface.mapCanvas().extent() canvasCrs = self.canvas.mapSettings().destinationCrs() if settings.bBoxCrs == 0 and canvasCrs != epsg4326: transform = QgsCoordinateTransform(canvasCrs, epsg4326, QgsProject.instance()) p1x, p1y = transform.transform(float(extent.xMinimum()), float(extent.yMinimum())) p2x, p2y = transform.transform(float(extent.xMaximum()), float(extent.yMaximum())) extent.set(p1x, p1y, p2x, p2y) delim = settings.bBoxDelimiter prefix = settings.bBoxPrefix suffix = settings.bBoxSuffix precision = settings.bBoxDigits outStr = '' minX = extent.xMinimum() minY = extent.yMinimum() maxX = extent.xMaximum() maxY = extent.yMaximum() if settings.bBoxFormat == 0: # minX,minY,maxX,maxY - using the delimiter outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format( minX, delim, minY, delim, maxX, delim, maxY, prec=precision) elif settings.bBoxFormat == 1: # minX,maxX,minY,maxY - Using the selected delimiter' outStr = '{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}{}{:.{prec}f}'.format( minX, delim, maxX, delim, minY, delim, maxY, prec=precision) elif settings.bBoxFormat == 2: # x1 y1,x2 y2,x3 y3,x4 y4,x1 y1 - Polygon format outStr = '{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f}'.format( minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision) elif settings.bBoxFormat == 3: # x1,y1 x2,y2 x3,y3 x4,y4 x1,y1 - Polygon format outStr = '{:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f} {:.{prec}f},{:.{prec}f}'.format( minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY, prec=precision) elif settings.bBoxFormat == 4: # WKT Polygon outStr = extent.asWktPolygon() elif settings.bBoxFormat == 5: # bbox: [minX, minY, maxX, maxY] - MapProxy outStr = 'bbox: [{}, {}, {}, {}]'.format(minX, minY, maxX, maxY) elif settings.bBoxFormat == 6: # bbox: [minX, minY, maxX, maxY] - MapProxy outStr = 'bbox={},{},{},{}'.format(minX, minY, maxX, maxY) outStr = '{}{}{}'.format(prefix, outStr, suffix) clipboard = QApplication.clipboard() clipboard.setText(outStr) self.iface.messageBar().pushMessage( "", "'{}' copied to the clipboard".format(outStr), level=Qgis.Info, duration=4) def setShowMapTool(self): '''Set the focus of the external map tool and check it''' self.externMapAction.setChecked(True) self.canvas.setMapTool(self.showMapTool) def showZoomToDialog(self): '''Show the zoom to docked widget.''' self.zoomToDialog.show() def multiZoomTo(self): '''Display the Multi-zoom to dialog box''' self.multiZoomDialog.show() def field2geom(self): '''Convert layer containing a point x & y coordinate to a new point layer''' results = processing.execAlgorithmDialog('latlontools:field2geom', {}) def geom2Field(self): '''Convert layer geometry to a text string''' results = processing.execAlgorithmDialog('latlontools:geom2field', {}) def toMGRS(self): '''Display the to MGRS dialog box''' results = processing.execAlgorithmDialog('latlontools:point2mgrs', {}) def MGRStoLayer(self): '''Display the to MGRS dialog box''' results = processing.execAlgorithmDialog('latlontools:mgrs2point', {}) def toPlusCodes(self): results = processing.execAlgorithmDialog('latlontools:point2pluscodes', {}) def PlusCodestoLayer(self): results = processing.execAlgorithmDialog('latlontools:pluscodes2point', {}) def settings(self): '''Show the settings dialog box''' self.settingsDialog.show() def help(self): '''Display a help page''' url = QUrl.fromLocalFile(os.path.dirname(__file__) + "/index.html").toString() webbrowser.open(url, new=2) def settingsChanged(self): # Settings may have changed so we need to make sure the zoomToDialog window is configured properly self.zoomToDialog.configure() self.multiZoomDialog.settingsChanged() def zoomTo(self, srcCrs, lat, lon): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(srcCrs, canvasCrs, QgsProject.instance()) x, y = transform.transform(float(lon), float(lat)) rect = QgsRectangle(x, y, x, y) self.canvas.setExtent(rect) pt = QgsPointXY(x, y) self.highlight(pt) self.canvas.refresh() return pt def highlight(self, point): currExt = self.canvas.extent() leftPt = QgsPoint(currExt.xMinimum(), point.y()) rightPt = QgsPoint(currExt.xMaximum(), point.y()) topPt = QgsPoint(point.x(), currExt.yMaximum()) bottomPt = QgsPoint(point.x(), currExt.yMinimum()) horizLine = QgsGeometry.fromPolyline([leftPt, rightPt]) vertLine = QgsGeometry.fromPolyline([topPt, bottomPt]) self.crossRb.reset(QgsWkbTypes.LineGeometry) self.crossRb.addGeometry(horizLine, None) self.crossRb.addGeometry(vertLine, None) QTimer.singleShot(700, self.resetRubberbands) def resetRubberbands(self): self.crossRb.reset() def digitizeClicked(self): if self.digitizerDialog == None: from .digitizer import DigitizerWidget self.digitizerDialog = DigitizerWidget(self, self.iface, self.iface.mainWindow()) self.digitizerDialog.show() def currentLayerChanged(self): layer = self.iface.activeLayer() if layer != None: try: layer.editingStarted.disconnect(self.layerEditingChanged) except: pass try: layer.editingStopped.disconnect(self.layerEditingChanged) except: pass if isinstance(layer, QgsVectorLayer): layer.editingStarted.connect(self.layerEditingChanged) layer.editingStopped.connect(self.layerEditingChanged) self.enableDigitizeTool() def layerEditingChanged(self): self.enableDigitizeTool() def enableDigitizeTool(self): self.digitizeAction.setEnabled(False) layer = self.iface.activeLayer() if layer != None and isinstance(layer, QgsVectorLayer) and ( layer.geometryType() == QgsWkbTypes.PointGeometry) and layer.isEditable(): self.digitizeAction.setEnabled(True) else: if self.digitizerDialog != None: self.digitizerDialog.close()
class RotateRasterMapTool(QgsMapToolEmitPoint): def __init__(self, iface): self.iface = iface self.canvas = iface.mapCanvas() QgsMapToolEmitPoint.__init__(self, self.canvas) self.rasterShadow = RasterShadowMapCanvasItem(self.canvas) self.rubberBandExtent = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandExtent.setColor(Qt.red) self.rubberBandExtent.setWidth(1) # In case of rotation around pressed point (ctrl) # Use rubberBand for displaying an horizontal line. self.rubberBandDisplacement = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.setColor(Qt.red) self.rubberBandDisplacement.setWidth(1) self.reset() def setLayer(self, layer): self.layer = layer def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() self.layer = None def canvasPressEvent(self, e): self.startY = e.pos().y() self.endY = self.startY self.isEmittingPoint = True self.height = self.canvas.height() modifiers = QApplication.keyboardModifiers() self.isRotationAroundPoint = bool(modifiers & Qt.ControlModifier) self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isLayerVisible = isLayerVisible(self.iface, self.layer) setLayerVisible(self.iface, self.layer, False) rotation = self.computeRotation() self.showRotation(rotation) self.layer.history.append({ "action": "rotation", "rotation": None, "center": self.layer.center }) # rotation set def canvasReleaseEvent(self, e): self.isEmittingPoint = False self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) self.rasterShadow.reset() rotation = self.computeRotation() if self.isRotationAroundPoint: self.layer.moveCenterFromPointRotate(self.startPoint, rotation, 1, 1) val = self.layer.rotation + rotation if val > 180: val = val - 360 # repainting is performed within the setValue trigger event. self.iface.mainWindow().findChild( QDoubleSpinBox, 'FreehandRasterGeoreferencer_spinbox').setValue(val) setLayerVisible(self.iface, self.layer, self.isLayerVisible) def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endY = e.pos().y() rotation = self.computeRotation() self.showRotation(rotation) self.endPoint = self.toMapCoordinates(e.pos()) def computeRotation(self): if self.isRotationAroundPoint: dX = self.endPoint.x() - self.startPoint.x() dY = self.endPoint.y() - self.startPoint.y() return math.degrees(math.atan2(-dY, dX)) else: dY = self.endY - self.startY return 90.0 * dY / self.height def showRotation(self, rotation): if self.isRotationAroundPoint: cornerPoints = self.layer.transformedCornerCoordinatesFromPoint( self.startPoint, rotation, 1, 1) self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaRotationFromPoint( rotation, self.startPoint, True) self.rasterShadow.show() self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry) point0 = QgsPointXY(self.startPoint.x() + 10, self.startPoint.y()) point1 = QgsPointXY(self.startPoint.x(), self.startPoint.y()) point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y()) self.rubberBandDisplacement.addPoint(point0, False) self.rubberBandDisplacement.addPoint(point1, False) self.rubberBandDisplacement.addPoint(point2, True) # true to update canvas self.rubberBandDisplacement.show() else: center, originalRotation, xScale, yScale = \ self.layer.transformParameters() newRotation = rotation + originalRotation cornerPoints = self.layer.transformedCornerCoordinates( center, newRotation, xScale, yScale) self.rasterShadow.reset(self.layer) self.rasterShadow.setDeltaRotation(rotation, True) self.rasterShadow.show() self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry) for point in cornerPoints: self.rubberBandExtent.addPoint(point, False) # for closing self.rubberBandExtent.addPoint(cornerPoints[0], True) self.rubberBandExtent.show()