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 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 DrawPolygonMapTool(QgsMapTool): polygonSelected = pyqtSignal(object) def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.extent = None self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setFillColor(RB_FILL) self.rubberBand.setStrokeColor(RB_STROKE) self.rubberBand.setWidth(1) self.vertex_count = 1 # two points are dropped initially def canvasReleaseEvent(self, event): if event.button() == Qt.RightButton: if self.rubberBand is None: return # TODO: validate geom before firing signal self.extent.removeDuplicateNodes() self.polygonSelected.emit(self.extent) self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) del self.rubberBand self.rubberBand = None self.vertex_count = 1 # two points are dropped initially return elif event.button() == Qt.LeftButton: if self.rubberBand is None: self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setFillColor(RB_FILL) self.rubberBand.setStrokeColor(RB_STROKE) self.rubberBand.setWidth(1) self.rubberBand.addPoint(event.mapPoint()) self.extent = self.rubberBand.asGeometry() self.vertex_count += 1 def canvasMoveEvent(self, event): if self.rubberBand is None: pass elif not self.rubberBand.numberOfVertices(): pass elif self.rubberBand.numberOfVertices() == self.vertex_count: if self.vertex_count == 2: mouse_vertex = self.rubberBand.numberOfVertices() - 1 self.rubberBand.movePoint(mouse_vertex, event.mapPoint()) else: self.rubberBand.addPoint(event.mapPoint()) else: mouse_vertex = self.rubberBand.numberOfVertices() - 1 self.rubberBand.movePoint(mouse_vertex, event.mapPoint()) def deactivate(self): QgsMapTool.deactivate(self) if self.rubberBand is not None: self.rubberBand.reset(QgsWkbTypes.PolygonGeometry) self.deactivated.emit()
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 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
class AddPipeTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.first_click = False self.rubber_band = QgsRubberBand(self.canvas(), False) self.rubber_band.setColor(QColor(255, 128, 128)) self.rubber_band.setWidth(1) self.rubber_band.setBrushStyle(Qt.Dense4Pattern) self.rubber_band.reset() self.snapper = None self.snapped_feat_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = None self.diameter_dialog = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = False if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) last_ix = self.rubber_band.numberOfVertices() self.rubber_band.movePoint(last_ix - 1, (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt)) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') # Mouse not clicked: snapping to closest vertex match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # Pipe starts from an existing vertex self.snapped_feat_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() # else: # It's a new, isolated pipe self.snapped_vertex = None self.snapped_feat_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): # if not self.mouse_clicked: # return if event.button() == Qt.LeftButton: # Update rubber bands self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) if self.first_click: self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) self.first_click = not self.first_click elif event.button() == Qt.RightButton: # try: pipe_band_geom = self.rubber_band.asGeometry() # No rubber band geometry and feature snapped: pop the context menu if self.rubber_band.size( ) == 0 and self.snapped_feat_id is not None: menu = QMenu() section_action = menu.addAction('Section...') # TODO: softcode diameter_action = menu.addAction( 'Change diameter...') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) pipe_ft = vector_utils.get_feats_by_id(self.params.pipes_vlay, self.snapped_feat_id)[0] if action == section_action: if self.params.dem_rlay is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'No DEM selected. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode else: # Check whether the pipe is all inside the DEM pipe_pts = pipe_ft.geometry().asPolyline() for pt in pipe_pts: if not self.params.dem_rlay.extent().contains(pt): self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Some pipe vertices fall outside of the DEM. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode return # Check whether the start/end nodes have an elevation value (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes( self.params, pipe_ft.geometry()) start_node_elev = start_node_ft.attribute( Junction.field_name_elev) end_node_elev = end_node_ft.attribute( Junction.field_name_elev) if start_node_elev == NULL or end_node_elev == NULL: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Missing elevation value in start or end node attributes. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode return pipe_dialog = PipeSectionDialog( self.iface.mainWindow(), self.iface, self.params, pipe_ft) pipe_dialog.exec_() elif action == diameter_action: old_diam = pipe_ft.attribute(Pipe.field_name_diameter) self.diameter_dialog = DiameterDialog( self.iface.mainWindow(), self.params, old_diam) self.diameter_dialog.exec_() # Exec creates modal dialog new_diameter = self.diameter_dialog.get_diameter() if new_diameter is None: return # Update pipe diameter vector_utils.update_attribute(self.params.pipes_vlay, pipe_ft, Pipe.field_name_diameter, new_diameter) # Check if a valve is present adj_valves = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, pipe_ft, True, True, False) if adj_valves['valves']: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Valves detected on the pipe: need to update their diameters too.', Qgis.Warning, 5) # TODO: softcode return # There's a rubber band: create new pipe if pipe_band_geom is not None: #XPSS ADDED - transform rubber band CRS canvas_crs = self.canvas().mapSettings().destinationCrs() #pipes_vlay = Parameters().pipes_vlay() #print(pipes_vlay) qepanet_crs = self.params.pipes_vlay.sourceCrs() crs_transform = QgsCoordinateTransform(canvas_crs, qepanet_crs, QgsProject.instance()) pipe_band_geom.transform(crs_transform) # Finalize line rubberband_pts = pipe_band_geom.asPolyline()[:-1] if len(rubberband_pts) == 0: self.iface.mapCanvas().refresh() return rubberband_pts = self.remove_duplicated_point(rubberband_pts) if len(rubberband_pts) < 2: self.rubber_band.reset() return # Check whether the pipe points are located on existing nodes junct_nrs = [0] for p in range(1, len(rubberband_pts) - 1): overlapping_nodes = NetworkUtils.find_overlapping_nodes( self.params, rubberband_pts[p]) if overlapping_nodes['junctions'] or overlapping_nodes[ 'reservoirs'] or overlapping_nodes['tanks']: junct_nrs.append(p) junct_nrs.append(len(rubberband_pts) - 1) new_pipes_nr = len(junct_nrs) - 1 new_pipes_fts = [] new_pipes_eids = [] for np in range(new_pipes_nr): pipe_eid = NetworkUtils.find_next_id( self.params.pipes_vlay, Pipe.prefix) # TODO: softcode #demand = float(self.data_dock.txt_pipe_demand.text()) length_units = 'm' #TODO soft code diameter = float(self.data_dock.cbo_pipe_dia.\ currentText()) diameter_units = self.data_dock.cbo_pipe_dia_units.\ currentText() #loss = float(self.data_dock.txt_pipe_loss.text()) #status = self.data_dock.cbo_pipe_status.currentText() material = self.data_dock.cbo_pipe_mtl.currentText() roughness = float(self.data_dock.txt_roughness.text()) #pipe_desc = self.data_dock.txt_pipe_desc.text() #pipe_tag = self.data_dock.cbo_pipe_tag.currentText() num_edu = 1 zone_id = 0 velocity = 0 velocity_units = 'm/s' frictionloss = 0 frictionloss_units = 'm' pipe_ft = LinkHandler.create_new_pipe( self.params, pipe_eid, length_units, diameter, diameter_units, 0, roughness, " ", material, rubberband_pts[junct_nrs[np]:junct_nrs[np + 1] + 1], True, " ", " ", num_edu, zone_id, velocity, velocity_units, frictionloss, frictionloss_units) self.rubber_band.reset() new_pipes_fts.append(pipe_ft) new_pipes_eids.append(pipe_eid) # emitter_coeff_s = self.data_dock.txt_junction_emit_coeff.text() # # if emitter_coeff_s is None or emitter_coeff_s == '': # emitter_coeff = float(0) # else: # emitter_coeff = float(self.data_dock.txt_junction_emit_coeff.text()) # # Description # junction_desc = self.data_dock.txt_junction_desc.text() # # # Tag # junction_tag = self.data_dock.cbo_junction_tag.currentText() zone_end = 0 pressure = 0 pressure_units = self.data_dock.cbo_rpt_units_pressure.currentText( ) # Create start and end node, if they don't exist (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[0].geometry()) new_start_junction = None if not start_junction: new_start_junction = rubberband_pts[0] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_start_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', Qgis.Warning, 5) # TODO: softcode deltaz = float(0) #j_demand = float(self.data_dock.txt_junction_demand.text()) # pattern = self.data_dock.cbo_junction_pattern.itemData( # self.data_dock.cbo_junction_pattern.currentIndex()) # if pattern is not None: # pattern_id = pattern.id # else: # pattern_id = None NodeHandler.create_new_junction(self.params, new_start_junction, junction_eid, elev, 0, deltaz, None, 0, " ", " ", zone_end, pressure, pressure_units) (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[len(new_pipes_fts) - 1].geometry()) new_end_junction = None if not end_junction: new_end_junction = rubberband_pts[len(rubberband_pts) - 1] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_end_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', Qgis.Warning, 5) # TODO: softcode deltaz = float(0) # pattern = self.data_dock.cbo_junction_pattern.itemData( # self.data_dock.cbo_junction_pattern.currentIndex()) # if pattern is not None: # pattern_id = pattern.id # else: # pattern_id = None NodeHandler.create_new_junction(self.params, new_end_junction, junction_eid, elev, 0, deltaz, None, 0, " ", " ", zone_end, pressure, pressure_units) # If end or start node intersects a pipe, split it if new_start_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[0] and\ pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_start_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_start_junction) if new_end_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[-1] and\ pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_end_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_end_junction) self.iface.mapCanvas().refresh() # except Exception as e: # self.rubber_band.reset() # self.iface.messageBar().pushWarning('Cannot add new pipe to ' + self.params.pipes_vlay.name() + ' layer', repr(e)) # traceback.print_exc(file=sys.stdout) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.rubber_band.reset() def activate(self): self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() def deactivate(self): # QgsProject.instance().setSnapSettingsForLayer(self.params.junctions_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # # QgsProject.instance().setSnapSettingsForLayer(self.params.reservoirs_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # QgsProject.instance().setSnapSettingsForLayer(self.params.tanks_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # QgsProject.instance().setSnapSettingsForLayer(self.params.pipes_vlay.id(), # True, # QgsSnapper.SnapToSegment, # QgsTolerance.MapUnits, # 0, # True) # self.rubber_band.reset() self.data_dock.btn_add_pipe.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def remove_duplicated_point(self, pts): # This is needed because the rubber band sometimes returns duplicated points purged_pts = [pts[0]] for p in enumerate(list(range(len(pts) - 1)), 1): if pts[p[0]] == pts[p[0] - 1]: continue else: purged_pts.append(pts[p[0]]) return purged_pts def update_snapper(self): layers = { self.params.junctions_vlay: QgsSnappingConfig.Vertex, self.params.reservoirs_vlay: QgsSnappingConfig.Vertex, self.params.tanks_vlay: QgsSnappingConfig.Vertex, self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Needed by Observable def update(self, observable): self.update_snapper()
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 QgepMapToolAddFeature(QgsMapToolAdvancedDigitizing): """ Base class for adding features """ def __init__(self, iface, layer): QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) 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(1) self.tempRubberband = QgsRubberBand(iface.mapCanvas(), layer.geometryType()) self.tempRubberband.setColor(QColor("#ee5555")) self.tempRubberband.setWidth(1) self.tempRubberband.setLineStyle(Qt.DotLine) def activate(self): """ When activating the map tool """ QgsMapToolAdvancedDigitizing.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) def deactivate(self): """ On deactivating the map tool """ QgsMapToolAdvancedDigitizing.deactivate(self) self.canvas.unsetCursor() # pylint: disable=no-self-use def isZoomTool(self): """ This is no zoom tool """ return False # =========================================================================== # Events # =========================================================================== def cadCanvasReleaseEvent(self, event): """ Called when a mouse button is :param event: :return: """ if event.button() == Qt.RightButton: self.rightClicked(event) else: self.leftClicked(event) def leftClicked(self, event): """ When the canvas is left clicked we add a new point to the rubberband. :type event: QMouseEvent """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) self.tempRubberband.reset() def rightClicked(self, _): """ On a right click we create a new feature from the existing rubberband and show the add dialog """ 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 cadCanvasMoveEvent(self, event): """ When the mouse is moved the rubberband needs to be updated :param event: The coordinates etc. """ # When a generated event arrives it's a QMoveEvent... No idea why, but this prevents from an exception try: QgsMapToolAdvancedDigitizing.cadCanvasMoveEvent(self, event) mousepos = event.mapPoint() self.tempRubberband.movePoint(mousepos) except TypeError: pass
class DsgLineTool(QgsMapTool): lineCreated = pyqtSignal(QgsGeometry) def __init__(self, canvas): super(DsgLineTool, self).__init__(canvas) self.canvas = canvas self.rubberBand = None self.reset() def deactivate(self): self.canvas.scene().removeItem(self.rubberBand) super(DsgLineTool, self).deactivate() def defineRubberBand(self): 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): if self.rubberBand: self.rubberBand.reset(QGis.Line) self.isEmittingPoint = False self.defineRubberBand() def canvasPressEvent(self, e): if self.isEmittingPoint: point = self.snapPoint(e.pos()) self.rubberBand.addPoint(point, True) else: self.reset() self.isEmittingPoint = True def canvasReleaseEvent(self, e): 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): if not self.isEmittingPoint: return point = self.snapPoint(e.pos()) self.rubberBand.movePoint(point) def snapPoint(self, p): m = self.canvas.snappingUtils().snapToMap(p) if m.isValid(): return m.point() else: return self.canvas.getCoordinateTransform().toMapCoordinates(p)
class QgepMapToolDigitizeDrainageChannel(QgsMapTool): ''' This is used to digitize a drainage channel. It lets you digitize two points and then creates a polygon based on these two points by adding an orthogonal offset at each side. Input: x==============x Output: ---------------- | | ---------------- Usage: Connect to the signals deactivated() and geometryDigitized() If geometryDigitized() is called you can use the member variable geometry which will contain a rectangle polygon deactivated() will be emited after a right click ''' geometryDigitized = pyqtSignal() 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(), QGis.Line) self.rubberband.setColor(QColor("#ee5555")) self.rubberband.setWidth(2) self.firstPoint = None self.messageBarItem = None self.geometry = None def activate(self): """ Map tool is activated """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) msgtitle = self.tr('Digitizing Drainage Channel') msg = self.tr('Digitize start and end point. Rightclick to abort.') self.messageBarItem = QgsMessageBar.createMessage(msgtitle, msg) self.iface.messageBar().pushItem(self.messageBarItem) def deactivate(self): """ Map tool is deactivated """ QgsMapTool.deactivate(self) self.iface.messageBar().popWidget(self.messageBarItem) try: self.iface.mapCanvas().scene().removeItem(self.rubberband) del self.rubberband except AttributeError: # Called repeatedly... bail out pass self.canvas.unsetCursor() def canvasMoveEvent(self, event): """ Mouse is moved: Update rubberband :param event: coordinates etc. """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.movePoint(mousepos) def canvasReleaseEvent(self, event): """ Canvas is released. This means: * start digitizing * stop digitizing (create a rectangle * if the Ctrl-modifier is pressed, ask for the rectangle width :param event: coordinates etc. """ if event.button() == Qt.RightButton: self.deactivate() else: mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) if self.firstPoint: # If the first point was set before, we are doing the second one lp1 = self.rubberband.asGeometry().asPolyline()[0] lp2 = self.rubberband.asGeometry().asPolyline()[1] width = 0.2 if QApplication.keyboardModifiers() & Qt.ControlModifier: dlg = QDialog() dlg.setLayout(QGridLayout()) dlg.layout().addWidget(QLabel(self.tr('Enter width'))) txt = QLineEdit('0.2') dlg.layout().addWidget(txt) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(bb) bb.accepted.connect(dlg.accept) bb.rejected.connect(dlg.reject) if dlg.exec_(): try: width = float(txt.text()) except ValueError: width = 0.2 length = math.sqrt(math.pow(lp1.x() - lp2.x(), 2) + math.pow(lp1.y() - lp2.y(), 2)) xd = lp2.x() - lp1.x() yd = lp2.y() - lp1.y() pt1 = QgsPoint(lp1.x() + width * (yd / length), lp1.y() - width * (xd / length)) pt2 = QgsPoint(lp1.x() - width * (yd / length), lp1.y() + width * (xd / length)) pt3 = QgsPoint(lp2.x() - width * (yd / length), lp2.y() + width * (xd / length)) pt4 = QgsPoint(lp2.x() + width * (yd / length), lp2.y() - width * (xd / length)) self.geometry = QgsGeometry.fromPolygon([[pt1, pt2, pt3, pt4, pt1]]) self.geometryDigitized.emit() self.firstPoint = mousepos
class ProfileTool(QgsMapTool): """ Narzędzie do tworzenia krzywej """ def __init__(self, parent): canvas = iface.mapCanvas() super(ProfileTool, self).__init__(canvas) set_cursor(self) self.editing = False self.parent = parent self.task = None self.tempGeom = QgsRubberBand(canvas, QgsWkbTypes.LineGeometry) self.tempGeom.setColor(QColor('red')) self.tempGeom.setWidth(2) self.tempLine = QgsRubberBand(canvas, QgsWkbTypes.LineGeometry) self.tempLine.setColor(QColor('red')) self.tempLine.setWidth(2) self.tempLine.setLineStyle(Qt.DotLine) def keyPressEvent(self, e): if e.key() == Qt.Key_Delete: pointsCount = self.tempLine.numberOfVertices() if pointsCount > 2 and self.editing: self.tempGeom.removePoint(pointsCount-2) self.tempLine.removePoint(pointsCount-2) len_m = self.calculateDistance(self.tempGeom.asGeometry()) self.parent.dsbLineLength.setValue(len_m) if self.tempGeom.numberOfVertices() == 1: self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.tempLine.reset(QgsWkbTypes.LineGeometry) self.parent.dsbLineLength.setValue(0) else: self.reset() elif e.key() == Qt.Key_Escape: self.reset() def canvasMoveEvent(self, e): #Poruszanie" wierzchołkiem linii tymczasowej zgodnie z ruchem myszki if self.tempGeom.numberOfVertices()>1: point = e.snapPoint() self.tempLine.movePoint(point) def canvasReleaseEvent(self, e): point = e.snapPoint() if self.task: self.parent.on_message.emit('Trwa genrowanie profilu. Aby wygenerować następny poczekaj na pobranie danych', Qgis.Warning, 4) return if e.button() == int(Qt.LeftButton): #Dodawanie kolejnych wierzchołków if not self.editing: #Nowy obiekt, pierwszy wierzchołek self.tempLine.reset(QgsWkbTypes.LineGeometry) self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.editing = True self.tempGeom.addPoint(point) self.tempLine.addPoint(point) len_m = self.calculateDistance(self.tempGeom.asGeometry()) self.parent.dsbLineLength.setValue(len_m) elif e.button() == int(Qt.RightButton): if self.tempGeom.numberOfVertices() < 2: return #Zakończenie rysowania obiektu self.tempLine.reset() self.editing = False geometry = self.tempGeom.asGeometry() errors = geometry.validateGeometry() if errors: #Niepoprawna geometria for error in errors: if self.tempGeom.numberOfVertices() > 2: self.parent.on_message.emit('Niepoprawna geometria', Qgis.Critical, 4) self.tempGeom.reset() return self.get_interval() def get_interval(self): interval, ok = QInputDialog.getDouble(self.parent, 'Podaj interwał', 'Interwał [m]:') if not ok: self.reset() return geom = self.tempGeom.asGeometry() activeCrs = QgsProject.instance().crs().authid() fromCrs = QgsCoordinateReferenceSystem(activeCrs) toCrs = QgsCoordinateReferenceSystem(2180) transformation = QgsCoordinateTransform(fromCrs, toCrs, QgsProject.instance()) geom.transform(transformation) meters_len = geom.length() if meters_len <= interval: self.parent.on_message.emit('Długość linii krótsza lub równa podanemu interwałowi', Qgis.Critical, 5) self.reset() return try: num_points = meters_len/interval except ZeroDivisionError: self.parent.on_message.emit('Interwał musi być większy od 0', Qgis.Critical, 4) self.reset() return points_on_line = [] max_interval = 0 intervals = [] for i in range(int(num_points)+1): pt = geom.interpolate(float(max_interval)) points_on_line.append(pt) intervals.append(max_interval) max_interval += interval data = {'points':points_on_line, 'intervals':intervals} self.task = QgsTask.fromFunction('Pobieranie wysokości dla przekroju...', self.generateProfileFromPoints, data=data) QgsApplication.taskManager().addTask(self.task) def generateProfileFromPoints(self, task: QgsTask, data): points_on_line = data.get('points') intervals = data.get('intervals') heights = [] total = 100/len(points_on_line) for idx, pt in enumerate(points_on_line): height = self.parent.getHeight(pt, special=True) heights.append(height) try: self.task.setProgress( idx*total ) except AttributeError as e: pass if heights and intervals: self.fillTable(heights, intervals) self.parent.on_message.emit('Pomyślnie wygenerowano profil', Qgis.Success, 4) self.task = None def fillTable(self, heights, intervals): for idx, interval in enumerate(intervals): self.parent.twData.setRowCount(idx+1) self.parent.twData.setItem(idx, 0, QTableWidgetItem(f'{interval}')) self.parent.twData.setItem(idx, 1, QTableWidgetItem(heights[idx])) def calculateDistance(self, geometry): distanceArea = QgsDistanceArea() distanceArea.setEllipsoid('GRS80') distanceArea.setSourceCrs(QgsProject.instance().crs(), QgsCoordinateTransformContext()) length = distanceArea.measureLength(geometry) result = distanceArea.convertLengthMeasurement(length, QgsUnitTypes.DistanceMeters) return result def reset(self): self.tempLine.reset(QgsWkbTypes.LineGeometry) self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.parent.dsbLineLength.setValue(0) self.parent.twData.setRowCount(0) def deactivate(self): self.reset() self.parent.dsbLineLength.setEnabled(False) self.button().setChecked(False)
class QGISRedMoveNodesTool(QgsMapTool): ownMainLayers = [ "Pipes", "Valves", "Pumps", "Junctions", "Tanks", "Reservoirs", "Demands", "Sources" ] myNodeLayers = ["Junctions", "Tanks", "Reservoirs", "Demands", "Sources"] def __init__(self, button, iface, projectDirectory, netwName): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.toolbarButton = button self.snapper = None self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.vertexMarker.setColor(QColor(255, 87, 51)) self.vertexMarker.setIconSize(15) self.vertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(3) self.vertexMarker.hide() self.mousePoint = None self.mouseClicked = False self.clickedPoint = None self.objectSnapped = None self.selectedNodeFeature = None self.selectedNodeLayer = None self.adjacentFeatures = None self.newPositionVector = QgsVector(0, 0) self.rubberBand = None self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.newVertexMarker.setColor(QColor(55, 198, 5)) self.newVertexMarker.setIconSize(15) self.newVertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.newVertexMarker.setPenWidth(3) self.newVertexMarker.hide() def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) myLayers = [] # Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: myLayers.append(layer) if not layer.isEditable(): layer.startEditing() # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(1) # Vertex config.setMode(2) # All layers config.setTolerance(1) config.setUnits(2) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def deactivate(self): self.toolbarButton.setChecked(False) # End Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: if layer.isModified(): layer.commitChanges() else: layer.rollBack() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def getUniformedPath(self, path): return QGISRedUtils().getUniformedPath(path) def getLayerPath(self, layer): return QGISRedUtils().getLayerPath(layer) def generatePath(self, folder, fileName): return QGISRedUtils().generatePath(folder, fileName) def getLayers(self): return QGISRedUtils().getLayers() def findAdjacentElements(self, nodeGeometry): adjacentElements = {} layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layePath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layePath: adjacentFeatures = [] for feature in layer.getFeatures(): featureGeometry = feature.geometry() if layer.geometryType() == 0: # Point if self.areOverlapedPoints(nodeGeometry, featureGeometry): adjacentFeatures.append(feature) elif layer.geometryType() == 1: if featureGeometry.isMultipart(): for part in featureGeometry.get( ): # only one part first_vertex = part[0] last_vertex = part[-1] else: first_vertex = featureGeometry.get()[0] last_vertex = featureGeometry.get()[-1] firsVertex = QgsGeometry.fromPointXY( QgsPointXY(first_vertex.x(), first_vertex.y())) lastVertex = QgsGeometry.fromPointXY( QgsPointXY(last_vertex.x(), last_vertex.y())) if self.areOverlapedPoints(nodeGeometry, firsVertex) or\ self.areOverlapedPoints(nodeGeometry, lastVertex): adjacentFeatures.append(feature) if len(adjacentFeatures) > 0: adjacentElements[layer] = adjacentFeatures return adjacentElements def areOverlapedPoints(self, point1, point2): tolerance = 0.1 if point1.distance(point2) < tolerance: return True else: return False def createRubberBand(self, points): myPoints = points if isinstance(points[0], QgsPointXY): myPoints = [] for p in points: myPoints.append(QgsPoint(p.x(), p.y())) self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None) self.rubberBand.setColor(QColor(55, 198, 5)) self.rubberBand.setWidth(1) self.rubberBand.setLineStyle(Qt.DashLine) self.newVertexMarker.setCenter(QgsPointXY(points[0].x(), points[0].y())) self.newVertexMarker.show() def updateRubberBand(self): newX = self.clickedPoint.x() + self.newPositionVector.x() newY = self.clickedPoint.y() + self.newPositionVector.y() self.rubberBand.movePoint(1, QgsPointXY(newX, newY)) self.newVertexMarker.setCenter(QgsPointXY(newX, newY)) def moveNodePoint(self, layer, nodeFeature, newPosition): if layer.isEditable(): layer.beginEditCommand('Move node') try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.moveVertex(newPosition.x(), newPosition.y(), nodeFeature.id(), 0) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def moveVertexLink(self, layer, feature, newPosition, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.moveVertex(newPosition.x(), newPosition.y(), feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() """Events""" def canvasPressEvent(self, event): if self.objectSnapped is None: self.clickedPoint = None return if event.button() == Qt.RightButton: self.mouseClicked = False self.clickedPoint = None if event.button() == Qt.LeftButton: self.mouseClicked = True self.clickedPoint = self.objectSnapped.point() self.selectedNodeFeature = None self.adjacentFeatures = None foundNode = False layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.myNodeLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: locatedPoint = self.snapper.locatorForLayer(layer) match = locatedPoint.nearestVertex( self.objectSnapped.point(), 1) if match.isValid(): featureId = match.featureId() request = QgsFeatureRequest().setFilterFid( featureId) node = list(layer.getFeatures(request)) self.selectedNodeLayer = layer foundNode = True if not foundNode: return self.selectedNodeFeature = QgsFeature(node[0]) self.adjacentFeatures = self.findAdjacentElements( self.selectedNodeFeature.geometry()) self.createRubberBand( [self.objectSnapped.point(), self.objectSnapped.point()]) def canvasMoveEvent(self, event): self.mousePoint = self.toMapCoordinates(event.pos()) # Mouse not clicked if not self.mouseClicked: match = self.snapper.snapToMap(self.mousePoint) if match.isValid(): self.objectSnapped = match vertex = match.point() self.vertexMarker.setCenter(QgsPointXY(vertex.x(), vertex.y())) self.vertexMarker.show() else: self.objectSnapped = None self.selectedNodeFeature = None self.vertexMarker.hide() # Mouse clicked else: # Update rubber band if self.objectSnapped is not None and self.rubberBand is not None: snappedPoint = self.objectSnapped.point() self.newPositionVector = QgsVector( self.mousePoint.x() - snappedPoint.x(), self.mousePoint.y() - snappedPoint.y()) self.updateRubberBand() def canvasReleaseEvent(self, event): mousePoint = self.toMapCoordinates(event.pos()) if not self.mouseClicked: return if event.button() == 1: self.mouseClicked = False if self.objectSnapped is not None: if self.selectedNodeFeature is not None: for adjLayer in self.adjacentFeatures: for feature in self.adjacentFeatures[adjLayer]: if adjLayer.geometryType() == 0: # Point self.moveNodePoint(adjLayer, feature, mousePoint) else: nodeGeometry = self.selectedNodeFeature.geometry( ) featureGeometry = feature.geometry() if featureGeometry.isMultipart(): for part in featureGeometry.get( ): # only one part firstVertex = part[0] vertices = len(part) else: firstVertex = featureGeometry.get()[0] vertices = 2 firstPoint = QgsGeometry.fromPointXY( QgsPointXY(firstVertex.x(), firstVertex.y())) if self.areOverlapedPoints( nodeGeometry, firstPoint): index = 0 else: index = vertices - 1 self.moveVertexLink(adjLayer, feature, mousePoint, index) self.objectSnapped = None self.selectedNodeFeature = None self.iface.mapCanvas().refresh() # Remove vertex marker and rubber band self.vertexMarker.hide() self.iface.mapCanvas().scene().removeItem(self.rubberBand) self.newVertexMarker.hide()
class SignatureTool(QgsMapTool): """ On Double click it will callback with an array with a single pixel. On Single click without releasing it will draw a square and callback the starting and ending point in an array. On Single click with releasing it will start drawing a polygon and every subsequent single click will add a new vertex in the polygon. On Right click it will callback with an array with all the vertex in the polygon. On Escape it will clean up the array and start over. """ def __init__(self, canvas, layer, callback): QgsMapTool.__init__(self, canvas) self._canvas = canvas self._layer = layer self._callback = callback self._pixels = [] self._start_point = None self._mode = Mode.NONE self._rubber_band = QgsRubberBand(self._canvas) self._rubber_band.setColor(Qt.red) self._rubber_band.setWidth(1) self.parent().setCursor(Qt.CrossCursor) def getPoint(self, pos): x = pos.x() y = pos.y() return self._canvas.getCoordinateTransform().toMapCoordinates(x, y) def getRowCol(self, point): # clicked position on screen to map coordinates data_provider = self._layer.dataProvider() extent = data_provider.extent() width = data_provider.xSize() if data_provider.capabilities() \ & data_provider.Size else 1000 height = data_provider.ySize() if data_provider.capabilities() \ & data_provider.Size else 1000 xres = extent.width() / width yres = extent.height() / height if extent.xMinimum() <= point.x() <= extent.xMaximum() and \ extent.yMinimum() <= point.y() <= extent.yMaximum(): col = int(floor((point.x() - extent.xMinimum()) / xres)) row = int(floor((extent.yMaximum() - point.y()) / yres)) return (row, col) else: return None def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self._pixels = [] self.finish() def canvasMoveEvent(self, event): point = self.getPoint(event.pos()) if self._mode is Mode.SQUARE: if not point.compare(self._start_point): self._rubber_band.reset() self._rubber_band.addPoint(self._start_point, False) self._rubber_band.addPoint( QgsPointXY(self._start_point.x(), point.y()), False) self._rubber_band.addPoint(point, False) self._rubber_band.addPoint( QgsPointXY(point.x(), self._start_point.y()), False) self._rubber_band.closePoints() elif self._mode is Mode.POLYGON: self._rubber_band.movePoint( self._rubber_band.numberOfVertices() - 1, point) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: point = self.getPoint(event.pos()) if self._mode is Mode.SQUARE: if self._start_point.compare(point): self._mode = Mode.POLYGON self._rubber_band.addPoint(point) self._start_point = None else: self._pixels = [] # The last vertex is repeated for i in range(self._rubber_band.numberOfVertices() - 1): self._pixels.append( self.getRowCol(self._rubber_band.getPoint(0, i))) self.finish() elif self._mode is Mode.POLYGON: self._rubber_band.addPoint(point) def canvasPressEvent(self, event): if event.button() == Qt.LeftButton: point = self.getPoint(event.pos()) pixel = self.getRowCol(point) if self._mode is Mode.NONE: self._mode = Mode.SQUARE self._start_point = QgsPointXY(point.x(), point.y()) elif self._mode is Mode.POLYGON: self._rubber_band.removePoint( self._rubber_band.numberOfVertices() - 1) else: self._mode = Mode.POLYGON if pixel: self._rubber_band.addPoint(point) self._pixels.append(pixel) elif event.button() == Qt.RightButton: self.finish() def finish(self): self._canvas.unsetMapTool(self) self._rubber_band.reset() self._mode = Mode.NONE self._start_point = None if len(self._pixels) > 0: self._callback(self._layer.hiperqube_id(), self._pixels)
class RubberBand(): def __init__(self, iface): self.iface = iface def initGui(self): # cria uma ação que iniciará a configuração do plugin #self.myMapTool = QgsMapToolEmitPoint( self.iface.mapCanvas() ) self.initVariables() self.initSignals() def initVariables(self): self.coordinates = [] # Criação da action e da toolbar self.toolbar = self.iface.addToolBar("My_ToolBar") pai = self.iface.mainWindow() icon_path = ':/plugins/RubberBand/icon.png' self.action = QAction(QIcon(icon_path), u"Cria RubberBand de Polygon.", pai) self.action.setObjectName("Cria RubberBand de Polygon.") self.action.setStatusTip(None) self.action.setWhatsThis(None) self.action.setCheckable(True) self.toolbar.addAction(self.action) self.previousMapTool = self.iface.mapCanvas().mapTool() self.myMapTool = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.isEditing = 0 # self.vlyr = QgsVectorLayer("Polygon?crs=EPSG:31982", "temporary_polygons", "memory") # self.dprov = self.vlyr.dataProvider() # # Add field to virtual layer # self.dprov.addAttributes([QgsField("name", QVariant.String), # QgsField("size", QVariant.Double)]) # self.vlyr.updateFields() # Access ID # self.fields = self.dprov.fields() def initSignals(self): self.action.toggled.connect(self.initRubberBand) self.myMapTool.canvasClicked.connect(self.mouseClick) def initRubberBand(self, b): if b: self.myRubberBand = QgsRubberBand(self.iface.mapCanvas(), QGis.Polygon) color = QColor(78, 97, 114) color.setAlpha(190) self.myRubberBand.setColor(color) self.myRubberBand.setFillColor(QColor(255, 0, 0, 40)) self.myRubberBand.setBorderColor(QColor(255, 0, 0, 200)) # Set MapTool self.iface.mapCanvas().setMapTool(self.myMapTool) self.iface.mapCanvas().xyCoordinates.connect(self.mouseMove) else: self.disconnect() def disconnect(self): self.iface.mapCanvas().unsetMapTool(self.myMapTool) try: self.iface.mapCanvas().xyCoordinates.disconnect(self.mouseMove) except: pass try: self.myRubberBand.reset() except: pass def unChecked(self): pass def unload(self): self.disconnect() def mouseClick(self, currentPos, clickedButton): if clickedButton == Qt.LeftButton: # and myRubberBand.numberOfVertices() == 0: self.myRubberBand.addPoint(QgsPoint(currentPos)) self.coordinates.append(QgsPoint(currentPos)) self.isEditing = 1 elif clickedButton == Qt.RightButton and self.myRubberBand.numberOfVertices( ) > 2: self.isEditing = 0 # open input dialog (description, False) = QInputDialog.getText( self.iface.mainWindow(), "Description", "Description for Polygon at x and y", QLineEdit.Normal, 'My Polygon') #create feature and set geometry poly = QgsFeature() geomP = self.myRubberBand.asGeometry() poly.setGeometry(geomP) print geomP.exportToWkt() #set attributes # indexN = self.dprov.fieldNameIndex('name') # indexA = self.dprov.fieldNameIndex('size') # poly.setAttributes([QgsDistanceArea().measurePolygon(self.coordinates), indexA]) # poly.setAttributes([description, indexN]) # add feature # self.dprov.addFeatures([poly]) # self.vlyr.updateExtents() #add layer # self.vlyr.triggerRepaint() # QgsMapLayerRegistry.instance().addMapLayers([self.vlyr]) # self.myRubberBand.reset(QGis.Polygon) def mouseMove(self, currentPos): if self.isEditing == 1: self.myRubberBand.movePoint(QgsPoint(currentPos))
class LineMapTool(QgsMapTool): def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.index_action = index_action self.elem_type_type = self.settings.value('insert_values/'+str(index_action)+'_elem_type_type') QgsMapTool.__init__(self, self.canvas) self.setAction(action) # Set rubber band features self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) mFillColor = QColor(255, 0, 0); self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(2) self.reset() # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setIconSize(9) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Snapper self.snapper = QgsMapCanvasSnapper(self.canvas) # Control button state self.mCtrl = False self.started = False # Tracing options self.firstTimeOnSegment = True self.lastPointMustStay = False self.lastPoint = None # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) self.parent().setCursor(self.cursor) def keyPressEvent(self, event): ''' We need to know, if ctrl-key is pressed ''' if event.key() == Qt.Key_Control: self.mCtrl = True def keyReleaseEvent(self, event): ''' Ctrl-key is released ''' if event.key() == Qt.Key_Control: self.mCtrl = False # Remove the last added point when the delete key is pressed if event.key() == Qt.Key_Backspace: self.rubberBand.removeLastPoint() def reset(self): self.start_point = self.end_point = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Line) ''' QgsMapTools inherited event functions ''' def canvasPressEvent(self, event): ''' On left click, we add a point ''' if event.button() == 1: # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() # Declare, that are we going to work self.started = True if layer <> None: x = event.pos().x() y = event.pos().y() selPoint = QPoint(x,y) # Check something snapped (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable # The point we want to have, is either from snapping result if result <> []: point = result[0].snappedVertex # If we snapped something, it's either a vertex if result[0].snappedVertexNr <> -1: self.firstTimeOnSegment = True # Or a point on a segment, so we have to declare, that a point on segment is found else: self.firstTimeOnSegment = False # Or its some point from out in the wild else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.firstTimeOnSegment = True # Bring the rubberband to the cursor i.e. the clicked point self.rubberBand.movePoint(point) # Set a new point to go on with self.appendPoint(point) # Try to remember that this point was on purpose i.e. clicked by the user self.lastPointMustStay = True self.firstTimeOnSegment = True def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Snapping (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> []: point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() # Check tracing if self.started: # Only if the ctrl key is pressed if self.mCtrl == True: # So if we have found a snapping if result <> []: # If it is a vertex, not a point on a segment if result[0].snappedVertexNr <> -1: self.rubberBand.movePoint(point) self.appendPoint(point) self.lastPointMustStay = True # The next point found, may be on a segment self.firstTimeOnSegment = True # We are on a segment else: self.rubberBand.movePoint(point) # If we are on a new segment, we add the point in any case if self.firstTimeOnSegment: self.appendPoint(point) self.lastPointMustStay = True self.firstTimeOnSegment = False # if we are not on a new segment, we have to test, if this point is really needed else: # but only if we have already enough points if self.rubberBand.numberOfVertices() >=3: num_vertexs = self.rubberBand.numberOfVertices() lastRbP = self.rubberBand.getPoint(0, num_vertexs-2) nextToLastRbP = self.rubberBand.getPoint(0, num_vertexs-3) if not self.pointOnLine(lastRbP, nextToLastRbP, QgsPoint(point)): self.appendPoint(point) self.lastPointMustStay = False else: if not self.lastPointMustStay: self.rubberBand.removeLastPoint() self.rubberBand.movePoint(point) else: self.appendPoint(point) self.lastPointMustStay = False self.firstTimeOnSegment = False else: #if nothing specials happens, just update the rubberband to the cursor position point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform (), x, y) self.rubberBand.movePoint(point) else: ''' In "not-tracing" state, just update the rubberband to the cursor position but we have still to snap to act like the "normal" digitize tool ''' if result <> []: point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, event): ''' With right click the digitizing is finished ''' if event.button() == 2: # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() x = event.pos().x() y = event.pos().y() if layer <> None and self.started == True: selPoint = QPoint(x,y) (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable if result <> []: point = result[0].snappedVertex #@UnusedVariable else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) #@UnusedVariable self.sendGeometry() def appendPoint(self, point): ''' Don't add the point if it is identical to the last point we added ''' if not (self.lastPoint == point) : self.rubberBand.addPoint(point) self.lastPoint = QgsPoint(point) else: pass def sendGeometry(self): #layer = self.canvas.currentLayer() layer = self.iface.activeLayer() coords = [] self.rubberBand.removeLastPoint() if QGis.QGIS_VERSION_INT >= 10700: [coords.append(self.rubberBand.getPoint(0, i)) for i in range(self.rubberBand.numberOfVertices())] else: [coords.append(self.rubberBand.getPoint(0,i)) for i in range(1,self.rubberBand.numberOfVertices())] # On the Fly reprojection, not necessary any more, mapToLayerCoordinates is clever enough on its own #layerEPSG = layer.srs().epsg() #projectEPSG = self.canvas.mapRenderer().destinationSrs().epsg() #if layerEPSG != projectEPSG: coords_tmp = coords[:] coords = [] for point in coords_tmp: transformedPoint = self.canvas.mapRenderer().mapToLayerCoordinates( layer, point ); coords.append(transformedPoint) # Filter duplicated points coords_tmp = coords[:] coords = [] lastPt = None for pt in coords_tmp: if (lastPt <> pt) : coords.append(pt) lastPt = pt # Add geometry to feature. g = QgsGeometry().fromPolyline(coords) self.rubberBand.reset(QGis.Line) self.started = False # Write the feature self.createFeature(g) def createFeature(self, geom): # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() provider = layer.dataProvider() f = QgsFeature() if (geom.isGeosValid()): f.setGeometry(geom) else: reply = QMessageBox.question(self.iface.mainWindow(), 'Feature not valid', "The geometry of the feature you just added isn't valid. Do you want to use it anyway?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: f.setGeometry(geom) else: return False # Add attribute fields to feature. fields = layer.pendingFields() try: #API-Break 1.8 vs. 2.0 handling attr = f.initAttributes(len(fields)) #@UnusedVariable for i in range(len(fields)): f.setAttribute(i, provider.defaultValue(i)) except AttributeError: #<=1.8 # Add attributefields to feature. for i in fields: f.addAttribute(i, provider.defaultValue(i)) idx = layer.fieldNameIndex('epa_type') f[idx] = self.elem_type_type # Upload changes layer.startEditing() layer.addFeature(f) # Control PostgreSQL exceptions boolOk = layer.commitChanges() # Update canvas self.canvas.refresh() # Capture edit exception if boolOk: # Spatial query to retrieve last added line, start searchingcandidates cands = layer.getFeatures(QgsFeatureRequest().setFilterRect(f.geometry().boundingBox())) # Iterate on candidates for line_feature in cands: if line_feature.geometry().equals(f.geometry()): # Highlight layer.setSelectedFeatures([line_feature.id()]) # Open form self.iface.openFeatureForm(layer, line_feature) break else: # Delete layer.rollBack() # User error msg = "Error adding PIPE: Typically this occurs if\n the first point is not located in a existing\n node or feature is out of the defined sectors." QMessageBox.information(None, "PostgreSQL error:", msg) def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): try: self.rubberBand.reset(QGis.Line) except AttributeError: pass
class ProfileTool(QgsMapTool): """ Narzędzie do tworzenia krzywej """ def __init__(self, parent): canvas = iface.mapCanvas() super(ProfileTool, self).__init__(canvas) set_cursor(self) self.editing = False self.parent = parent self.task = None #Konfiguracja geometrii tymczasowych self.tempGeom = QgsRubberBand(canvas, QgsWkbTypes.LineGeometry) self.tempGeom.setColor(QColor('red')) self.tempGeom.setWidth(2) self.tempLine = QgsRubberBand(canvas, QgsWkbTypes.LineGeometry) self.tempLine.setColor(QColor('red')) self.tempLine.setWidth(2) self.tempLine.setLineStyle(Qt.DotLine) def keyPressEvent(self, e): """ Usuwanie ostatniego dodanego punktu/segmentu lub czyszczenie całości """ if e.key() == Qt.Key_Delete: pointsCount = self.tempLine.numberOfVertices() if pointsCount > 2 and self.editing: self.tempGeom.removePoint(pointsCount - 2) self.tempLine.removePoint(pointsCount - 2) len_m = self.calculateDistance(self.tempGeom.asGeometry()) self.parent.dsbLineLength.setValue(len_m) if self.tempGeom.numberOfVertices() == 1: self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.tempLine.reset(QgsWkbTypes.LineGeometry) self.parent.dsbLineLength.setValue(0) else: self.reset() elif e.key() == Qt.Key_Escape: self.reset() def canvasMoveEvent(self, e): """ 'Poruszanie' wierzchołkiem linii tymczasowej zgodnie z ruchem myszki """ if self.tempGeom.numberOfVertices() > 1: point = e.snapPoint() self.tempLine.movePoint(point) def canvasReleaseEvent(self, e): """ Rysowanie obiektu """ point = e.snapPoint() if self.task is not None: self.parent.on_message.emit( 'Trwa genrowanie profilu. Aby wygenerować następny poczekaj na pobranie danych', Qgis.Warning, 4) return if e.button() == int(Qt.LeftButton): #Dodawanie kolejnych wierzchołków if not self.editing: #Nowy obiekt, pierwszy wierzchołek self.tempLine.reset(QgsWkbTypes.LineGeometry) self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.editing = True self.tempGeom.addPoint(point) self.tempLine.addPoint(point) len_m = self.calculateDistance(self.tempGeom.asGeometry()) self.parent.dsbLineLength.setValue(len_m) elif e.button() == int(Qt.RightButton): if self.tempGeom.numberOfVertices() < 2: return #Zakończenie rysowania obiektu self.tempLine.reset() self.editing = False geometry = self.tempGeom.asGeometry() errors = geometry.validateGeometry() if errors: #Niepoprawna geometria for error in errors: if self.tempGeom.numberOfVertices() > 2: self.parent.on_message.emit('Niepoprawna geometria', Qgis.Critical, 4) self.tempGeom.reset() return self.getInterval() def getInterval(self): """ Zebranie geometrii punktów na linii zgodnie z zadanym interwałem """ interval, ok = QInputDialog.getDouble(self.parent, 'Podaj interwał', 'Interwał [m]:') if not ok: self.reset() return geom = self.parent.transformGeometry( self.tempGeom.asGeometry(), current_crs=QgsProject.instance().crs().authid(), ) meters_len = geom.length() if meters_len <= interval: self.parent.on_message.emit( 'Długość linii krótsza lub równa podanemu interwałowi', Qgis.Critical, 5) self.reset() return try: num_points = meters_len / interval except ZeroDivisionError: self.parent.on_message.emit('Interwał musi być większy od 0', Qgis.Critical, 4) self.reset() return points_on_line = [] max_interval = 0 intervals = [] for i in range(int(num_points) + 1): pt = geom.interpolate(float(max_interval)).asPoint() points_on_line.append(f'{pt.y()}%20{pt.x()}') intervals.append(max_interval) max_interval += interval data = {'points': points_on_line, 'intervals': intervals} self.task = QgsTask.fromFunction( 'Pobieranie wysokości dla przekroju...', self.generateProfileFromPoints, data=data) QgsApplication.taskManager().addTask(self.task) def generateProfileFromPoints(self, task: QgsTask, data): """ Pobranie wysokości dla punktów na linii """ points_on_line = data.get('points') intervals = data.get('intervals') response = self.parent.getPointsHeights(points_on_line).split(',') heights = [] for r in response: _, height = r.rsplit(' ', 1) heights.append(height) if heights and intervals: self.fillTable(heights, intervals) self.parent.on_message.emit('Pomyślnie wygenerowano profil', Qgis.Success, 4) self.task = None def fillTable(self, heights, intervals): """ Wypełnienie tabelii interwałami i wysokościami dla nich """ for idx, interval in enumerate(intervals): try: self.parent.twData.setRowCount(idx + 1) self.parent.twData.setItem(idx, 0, QTableWidgetItem(f'{interval}')) self.parent.twData.setItem(idx, 1, QTableWidgetItem(heights[idx])) except: return def calculateDistance(self, geometry): """ Obliczenie długości linii w odpowiedniej jednostce """ distance_area = QgsDistanceArea() distance_area.setEllipsoid('GRS80') distance_area.setSourceCrs(QgsProject.instance().crs(), QgsCoordinateTransformContext()) length = distance_area.measureLength(geometry) result = distance_area.convertLengthMeasurement( length, QgsUnitTypes.DistanceMeters) return result def reset(self): """ Czyszczenie narzędzia """ self.tempLine.reset(QgsWkbTypes.LineGeometry) self.tempGeom.reset(QgsWkbTypes.LineGeometry) self.parent.dsbLineLength.setValue(0) self.parent.twData.setRowCount(0) def deactivate(self): """ Reagowanie zmiany aktywności narzędzia """ self.reset() self.parent.dsbLineLength.setEnabled(False) self.button().setChecked(False)
class ApisMapToolEmitPolygonAndPoint(QgsMapTool, ApisMapToolMixin): mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.rubberBand = None self.tempRubberBand = None self.vertexMarker = None self.capturedPoints = [] self.derivedPoint = None self.capturing = False self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.addVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def canvasMoveEvent(self, event): if self.tempRubberBand != None and self.capturing: mapPt = self.transformCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPt) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() event.ignore() if event.key() == Qt.Key_Escape: self.stopCapturing() self.clearScene() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def startCapturing(self): self.clearScene() self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setWidth(2) self.rubberBand.setFillColor(QColor(220, 0, 0, 120)) self.rubberBand.setBorderColor(QColor(220, 0, 0)) self.rubberBand.setLineStyle(Qt.DotLine) self.rubberBand.show() self.tempRubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.tempRubberBand.setWidth(2) self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0)) self.tempRubberBand.setBorderColor(QColor(220, 0, 0)) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setIconType(1) self.vertexMarker.setColor(QColor(220, 0, 0)) self.vertexMarker.setIconSize(16) self.vertexMarker.setPenWidth(3) self.vertexMarker.show() self.capturing = True def clearScene(self): if self.vertexMarker: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None def stopCapturing(self): if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.capturing = False self.capturedPoints = [] self.derivedPoint = None self.canvas.refresh() def addVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) self.rubberBand.addPoint(mapPt) self.capturedPoints.append(mapPt) bandSize = self.rubberBand.numberOfVertices() if bandSize > 2: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() self.tempRubberBand.reset(QGis.Polygon) firstPoint = self.rubberBand.getPoint(0, 0) self.tempRubberBand.addPoint(firstPoint) self.tempRubberBand.movePoint(mapPt) self.tempRubberBand.addPoint(mapPt) def removeLastVertex(self): if not self.capturing: return bandSize = self.rubberBand.numberOfVertices() tempBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if bandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if bandSize > 1: if tempBandSize > 1: point = self.rubberBand.getPoint(0, bandSize - 2) self.tempRubberBand.movePoint(tempBandSize - 2, point) else: self.tempRubberBand.reset(QGis.Polygon) bandSize = self.rubberBand.numberOfVertices() if bandSize < 3: self.vertexMarker.hide() else: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() del self.capturedPoints[-1] def getCapturedPolygon(self): polygon = self.capturedPoints if len(polygon) < 3: return None else: return polygon def getDerivedPoint(self): point = self.derivedPoint if point == None: return None else: return point def getPointGeometry(self, geom): p = QgsGeometry.fromPoint(geom) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None def getPolygonGeometry(self, geom): p = QgsGeometry.fromPolygon([geom]) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None
class MeasureAngleTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.middle_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) self.rubber_band_curve = QgsRubberBand(self.canvas) self.rubber_band_curve.setWidth(2) self.rubber_band_curve.setColor(QColor(255, 153, 0, 100)) crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): self.msglog.logMessage("") self.start_point = self.middle_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.rubber_band_curve.reset() def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point and self.middle_point: angle_start_to_middle = self.distance_calc.bearing( self.middle_point, self.start_point) angle_end_to_middle = self.distance_calc.bearing( self.middle_point, point) angle = degrees(angle_end_to_middle - angle_start_to_middle) if angle < -180: angle = 360 + angle elif angle > 180: angle = angle - 360 anglemsg = QMessageBox(self.parent()) anglemsg.finished.connect(self.deactivate) anglemsg.setWindowTitle("Measure angle tool") anglemsg.setText("Angle: {:.3F} º".format(abs(angle))) anglemsg.exec() self.finish() elif self.start_point: self.middle_point = point self.rubber_band.addPoint(self.middle_point) self.rubber_band_points.addPoint(self.middle_point) else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) if self.start_point and self.middle_point and not self.end_point: angle_start_to_middle = self.distance_calc.bearing( self.middle_point, self.start_point) angle_end_to_middle = self.distance_calc.bearing( self.middle_point, point) angle = degrees(angle_end_to_middle - angle_start_to_middle) if angle < -180: angle = 360 + angle elif angle > 180: angle = angle - 360 self.msglog.logMessage("") self.msglog.logMessage( "Current angle: {:.3F} º".format(abs(angle)), "Measure angle:", 0) self.rubber_band_curve.reset() # get the distance from center to point dist_mid_to_p = sqrt((point.x() - self.middle_point.x()) * (point.x() - self.middle_point.x()) + (point.y() - self.middle_point.y()) * (point.y() - self.middle_point.y())) dist_mid_to_start = sqrt( (self.start_point.x() - self.middle_point.x()) * (self.start_point.x() - self.middle_point.x()) + (self.start_point.y() - self.middle_point.y()) * (self.start_point.y() - self.middle_point.y())) # get angle angle_start = atan2(self.start_point.y() - self.middle_point.y(), self.start_point.x() - self.middle_point.x()) angle_p = atan2(point.y() - self.middle_point.y(), point.x() - self.middle_point.x()) # smaller distance if dist_mid_to_p < dist_mid_to_start: dist = dist_mid_to_p else: dist = dist_mid_to_start y_p = dist * sin(angle_p) x_p = dist * cos(angle_p) y_start = dist * sin(angle_start) x_start = dist * cos(angle_start) circular_ring = QgsCircularString() circular_ring = circular_ring.fromTwoPointsAndCenter( QgsPoint(self.middle_point.x() + x_start / 2, self.middle_point.y() + y_start / 2), QgsPoint(self.middle_point.x() + x_p / 2, self.middle_point.y() + y_p / 2), QgsPoint(self.middle_point.x(), self.middle_point.y()), True) circular_geometry = QgsGeometry(circular_ring) self.rubber_band_curve.addGeometry( circular_geometry, QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem.EpsgCrsId)) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
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.gt = None 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) 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 self.gt is not None and self._interaction.lineDrawer: self.drawLines.append([None, None, None]) return if self.gt is not None and self._interaction.measureDistance: self.drawMeasureDistance.append([None, None, None]) return if self.gt is not None and self._interaction.measureArea: self.drawMeasureArea.append([None, None, None]) return if self.gt 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() 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 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 self.gt = GetGCPGeoTransform() # Draw On Video draw.drawOnVideo(self.drawPtPos, self.drawLines, self.drawPolygon, self.drawMeasureDistance, self.drawMeasureArea, self.drawCesure, self.painter, self.surface, self.gt) # Draw On Video Object tracking test 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.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): """ @type event: QMouseEvent @param event: @return: """ # Magnifier mouseMoveEvent # Magnifier can move on black screen for show image borders if self._interaction.magnifier: if event.buttons(): 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 # Mouser 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 self.gt 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 = mgrs.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;'>" + \ ("%.3f" % Longitude) + "</span>" txt += "<span style='font-size:10pt; font-weight:bold;'> Lat : </span>" txt += "<span style='font-size:9pt; font-weight:normal;'>" + \ ("%.3f" % 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>") if not event.buttons(): return # 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 self.gt 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 self.gt 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 self.gt 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 self.gt 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 self.gt 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 MeasureAreaTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.middle_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) crs = self.canvas.mapSettings().destinationCrs() self.area_calc = QgsDistanceArea() self.area_calc.setSourceCrs(crs, QgsProject.instance().transformContext()) self.area_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): """ Reset log message and rubber band""" self.msglog.logMessage("") self.start_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point and event.button() == Qt.RightButton: multipoint = self.rubber_band.asGeometry() area = self.area_calc.measureArea(multipoint) anglemsg = QMessageBox(self.parent()) anglemsg.finished.connect(self.deactivate) anglemsg.setWindowTitle("Measure area tool") anglemsg.setText("Area: {} ".format( self.area_calc.formatArea(area, 3, QgsUnitTypes.AreaSquareMeters, True))) anglemsg.exec() self.finish() elif self.start_point: self.rubber_band.addPoint(point) self.rubber_band_points.addPoint(point) else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) multipoint = self.rubber_band.asGeometry() area = self.area_calc.measureArea(multipoint) self.msglog.logMessage("") self.msglog.logMessage( "Current area: {} ".format( self.area_calc.formatArea(area, 3, QgsUnitTypes.AreaSquareMeters, True)), "Measure Area:", 0) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
class QvPrint(QWidget): """Una classe del tipus QWidget que servirà per imprimir un area determinada. El widget conté un botó per imprimir, un per tornar a posicionar l'area d'impresió, i un comboBox per escollir l'escala. """ def __init__(self, project, canvas, poligon, parent=None): """Inicialització de la clase: Arguments: project {QgsProject().instance()} -- El projecte actiu canvas {QgsVectorLayer} -- El canvas sobre el que es coloca la rubberband. poligon {QgsPoligon} -- Poligon inicial. A revisar. """ # We inherit our parent's properties and methods. QWidget.__init__(self, parent) self.parent = parent # Creating a memory layer to draw later the rubberband. estatDirtybit = self.parent.canvisPendents self.layer = QgsVectorLayer('Point?crs=epsg:23031', "Capa temporal d'impressió", "memory") project.addMapLayer(self.layer, False) # We store safely the parameters as class variables. self.canvas = canvas self.project = project self.poligon = poligon # Offset inicial del rectangle d'impressió- TODO self.incX = 100 self.incY = 150 # Semafor per deixar fix el rectangle un cop fet click. self.pucMoure = True # Diccionari d'escales i proporcions que fixen el tamany del rectangle en pantalla. # Podria fer-se millor, pero Practicality beats Purity... self.dictEscales = { '100': 20, '200': 40, '250': 45, '500': 100, '1000': 200, '2000': 400, '2500': 450, '5000': 1000, '10000': 2000, '20000': 4000, '25000': 4500, '50000': 10000 } # We instanciate de PointTool tool, to wait for clicks # After that, we assign the tool to the canvas. rp = PointTool(self, self.canvas) canvas.setMapTool(rp) self.setupUI() self.rubberband = QgsRubberBand(self.canvas) self.rubberband.setColor(QColor(0, 0, 0, 50)) self.rubberband.setWidth(4) self.canvas.xyCoordinates.connect(self.mocMouse) self.pintarRectangle(self.poligon) self.rubberband.hide() self.parent.setDirtyBit(estatDirtybit) def setupUI(self): self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.layout.setContentsMargins(10, 20, 10, 20) self.layout.setSpacing(14) # self.layout.setAlignment(Qt.AlignTop) self.layoutTitol = QHBoxLayout() self.lblTitol = QLabel("Títol: ") self.leTitol = QLineEdit(self) self.leTitol.setText(self.parent.titolProjecte) self.layoutTitol.addWidget(self.lblTitol) self.layoutTitol.addWidget(self.leTitol) self.cbOrientacio = QComboBox(self) self.cbOrientacio.addItems(['Vertical', 'Horitzontal']) self.cbOrientacio.SelectedItem = "Vertical" self.cbOrientacio.setCurrentIndex(1) self.cbOrientacio.currentTextChanged.connect(self.canviOrientacio) self.lblCBOrientacio = QLabel("Orientació: ") self.layoutCBOrientacio = QHBoxLayout() self.layoutCBOrientacio.addWidget(self.lblCBOrientacio) self.layoutCBOrientacio.addWidget(self.cbOrientacio) self.combo = QComboBox(self) llistaEscales = [key for key in self.dictEscales] self.combo.addItems(llistaEscales) self.combo.currentTextChanged.connect(self.canviEscala) self.lblEscales = QLabel("Escales") self.layEscales = QHBoxLayout() self.layEscales.addWidget(self.lblEscales) self.layEscales.addWidget(self.combo) self.cbMida = QComboBox(self) self.cbMida.addItems(['A0', 'A1', 'A2', 'A3', 'A4']) self.cbMida.currentTextChanged.connect(self.canviEscala) self.cbMida.setCurrentIndex(4) self.lblCBmida = QLabel("Paper: ") self.layoutCBmida = QHBoxLayout() self.layoutCBmida.addWidget(self.lblCBmida) self.layoutCBmida.addWidget(self.cbMida) self.boto = QvPushButton(text='Generar PDF', destacat=True, parent=self) self.boto.clicked.connect(self.printPlanol) self.boto.setFixedWidth(220) self.boto2 = QvPushButton(text='Emmarcar zona a imprimir', parent=self) self.boto2.clicked.connect(self.potsMoure) self.boto2.setFixedWidth(220) self.nota = QLabel( "NOTA: Alguns navegadors web alteren l'escala d'impressió dels PDFs. Per màxima exactitud imprimiu des de l'Adobe Acrobat." ) styleheetLabel = ''' QLabel { color: grey; }''' self.nota.setStyleSheet(styleheetLabel) self.nota.setMaximumWidth(200) self.nota.setWordWrap(True) self.layout.addLayout(self.layoutTitol) self.layout.addLayout(self.layEscales) self.layout.addLayout(self.layoutCBmida) self.layout.addLayout(self.layoutCBOrientacio) self.layout.addWidget(self.boto2) self.layout.addWidget(self.boto) self.layout.addWidget(self.nota) # self.layout.addWidget(self.wFormat) # self.layout.addWidget(self.rbVertical) # self.layout.addWidget(self.rbHoritzontal) self.layout.addStretch() def potsMoure(self): # self.canvas.scene().removeItem(self.rubberband) self.pucMoure = True def canviEscala(self): self.pucMoure = True escala = int(self.dictEscales[self.combo.currentText()]) mida = self.cbMida.currentText() if mida == 'A3': escala *= math.sqrt(2) elif mida == 'A2': escala *= math.sqrt(2) * 2 elif mida == 'A1': escala *= math.sqrt(2) * 3 elif mida == 'A0': escala *= math.sqrt(2) * 4 if self.cbOrientacio.SelectedItem == "Horitzontal": self.incX = escala self.incY = escala * 1.5 else: self.incX = escala * 1.5 self.incY = escala def canviOrientacio(self): self.pucMoure = True if self.cbOrientacio.SelectedItem == "Vertical": self.cbOrientacio.SelectedItem = "Horitzontal" else: self.cbOrientacio.SelectedItem = "Vertical" self.incX, self.incY = self.incY, self.incX def canvasClickat(self): #??? print('Clickat, si') def mocMouse(self, p): if not self.isVisible(): self.rubberband.hide() self.pucMoure elif self.pucMoure: if self.canvas.rotation() == 0: self.posXY = [p.x() + self.incX / 2, p.y() + self.incY / 2] self.rubberband.movePoint( 0, QgsPointXY(p.x() + self.incX, p.y() + self.incY), 0) self.rubberband.movePoint(1, QgsPointXY(p.x() + self.incX, p.y()), 0) self.rubberband.movePoint(2, QgsPointXY(p.x(), p.y()), 0) self.rubberband.movePoint(3, QgsPointXY(p.x(), p.y() + self.incY), 0) self.rubberband.movePoint( 4, QgsPointXY(p.x() + self.incX, p.y() + self.incY), 0) else: alpha = math.radians(self.canvas.rotation()) beta = math.atan(self.incY / self.incX) d = math.sqrt(self.incX**2 + self.incY**2) self.posXY = [(2 * p.x() + d * math.cos(alpha + beta)) / 2, (2 * p.y() + d * math.sin(alpha + beta)) / 2] self.rubberband.movePoint( 0, QgsPointXY(p.x() + d * math.cos(alpha + beta), p.y() + d * math.sin(alpha + beta)), 0) self.rubberband.movePoint( 1, QgsPointXY(p.x() + self.incX * math.cos(alpha), p.y() + self.incX * math.sin(alpha)), 0) self.rubberband.movePoint(2, QgsPointXY(p.x(), p.y()), 0) self.rubberband.movePoint( 3, QgsPointXY( p.x() + self.incY * math.cos(math.radians(90 + 45)), p.y() + self.incY * math.sin(math.radians(90 + 45))), 0) self.rubberband.movePoint( 4, QgsPointXY(p.x() + d * math.cos(alpha + beta), p.y() + d * math.sin(alpha + beta)), 0) self.rubberband.show() def pintarRectangle(self, poligon): points = [ QgsPointXY(0, 0), QgsPointXY(0, 10), QgsPointXY(10, 10), QgsPointXY(0, 10), QgsPointXY(0, 0) ] poligono = QgsGeometry.fromRect(self.poligon) self.rubberband.setToGeometry(poligono, self.layer) def printPlanol(self): # # if self.checkRotacio.checkState(): # rotacio=44.75 # else: # rotacio=0 rotacio = self.canvas.rotation() if self.cbOrientacio.currentText() == "Vertical": if self.cbMida.currentText() == "A4": self.plantillaMapa = pathPlantilles + 'plantillaMapa.qpt' elif self.cbMida.currentText() == "A3": self.plantillaMapa = pathPlantilles + 'plantillaMapaA3.qpt' elif self.cbMida.currentText() == "A2": self.plantillaMapa = pathPlantilles + 'plantillaMapaA2.qpt' elif self.cbMida.currentText() == "A1": self.plantillaMapa = pathPlantilles + 'plantillaMapaA1.qpt' elif self.cbMida.currentText() == "A0": self.plantillaMapa = pathPlantilles + 'plantillaMapaA0.qpt' else: if self.cbMida.currentText() == "A4": self.plantillaMapa = pathPlantilles + 'plantillaMapaH.qpt' elif self.cbMida.currentText() == "A3": self.plantillaMapa = pathPlantilles + 'plantillaMapaA3H.qpt' elif self.cbMida.currentText() == "A2": self.plantillaMapa = pathPlantilles + 'plantillaMapaA2H.qpt' elif self.cbMida.currentText() == "A1": self.plantillaMapa = pathPlantilles + 'plantillaMapaA1H.qpt' elif self.cbMida.currentText() == "A0": self.plantillaMapa = pathPlantilles + 'plantillaMapaA0H.qpt' t = time.localtime() timestamp = time.strftime('%d-%b-%Y_%H%M%S', t) sortida = tempdir + 'sortida_' + timestamp self.imprimirPlanol(self.posXY[0], self.posXY[1], int(self.combo.currentText()), rotacio, self.cbMida.currentText(), self.plantillaMapa, sortida, 'PDF') QvApp().logRegistre('Impressió: ' + self.combo.currentText()) def imprimirPlanol(self, x, y, escala, rotacion, midaPagina, templateFile, fitxerSortida, tipusSortida): tInicial = time.time() template = QFile(templateFile) doc = QDomDocument() doc.setContent(template, False) layout = QgsLayout(self.project) # page=QgsLayoutItemPage(layout) # page.setPageSize(midaPagina) # layout.pageCollection().addPage(page) # layout.initializeDefaults() # p=layout.pageCollection().pages()[0] # p.setPageSize(midaPagina) context = QgsReadWriteContext() [items, ok] = layout.loadFromTemplate(doc, context) # p=layout.pageCollection().pages()[0] # p.setPageSize(midaPagina) if ok: refMap = layout.referenceMap() titol = layout.itemById('idNomMapa') dataMapa = layout.itemById('idData') if self.leTitol.text() != '': titol.setText(self.leTitol.text()) #comentat pk peta else: titol.setText('') try: t = time.localtime() dataMapa.setText(strftime('%b-%d-%Y %H:%M', t)) except: pass rect = refMap.extent() vector = QgsVector(x - rect.center().x(), y - rect.center().y()) rect += vector refMap.setExtent(rect) refMap.setScale(escala) refMap.setMapRotation(rotacion) #Depenent del tipus de sortida... exporter = QgsLayoutExporter(layout) # image_settings = exporter.ImageExportSettings() # image_settings.dpi = 30 # result = exporter.exportToImage('d:/dropbox/qpic/preview.png', image_settings) # imatge = QPixmap('d:/dropbox/qpic/preview.png') # self.ui.lblImatgeResultat.setPixmap(imatge) if tipusSortida == 'PDF': settings = QgsLayoutExporter.PdfExportSettings() settings.dpi = 300 settings.exportMetadata = False # fitxerSortida='d:/sortida_'+timestamp+'.PDF' fitxerSortida += '.PDF' result = exporter.exportToPdf( fitxerSortida, settings) #Cal desar el resultat (???) print(fitxerSortida) if tipusSortida == 'PNG': settings = QgsLayoutExporter.ImageExportSettings() settings.dpi = 300 # fitxerSortida='d:/sortida_'+timestamp+'.PNG' fitxerSortida += '.PNG' result = exporter.exportToImage( fitxerSortida, settings) #Cal desar el resultat (???) #Obra el document si està marcat checkObrirResultat QDesktopServices().openUrl(QUrl(fitxerSortida)) segonsEmprats = round(time.time() - tInicial, 1) #??? layersTemporals = self.project.mapLayersByName( "Capa temporal d'impressió") estatDirtybit = self.parent.canvisPendents for layer in layersTemporals: self.project.removeMapLayer(layer.id()) self.parent.setDirtyBit(estatDirtybit) def oculta(self): #Eliminem la capa temporal estatDirtybit = self.parent.canvisPendents layersTemporals = self.project.mapLayersByName( "Capa temporal d'impressió") for layer in layersTemporals: self.project.removeMapLayer(layer.id()) self.parent.setDirtyBit(estatDirtybit)
class MoveNodeMapTool(ParentMapTool): ''' Button 16. Move node Execute SQL function: 'gw_fct_node2arc' ''' def __init__(self, iface, settings, action, index_action, controller, srid): ''' Class constructor ''' # Call ParentMapTool constructor super(MoveNodeMapTool, self).__init__(iface, settings, action, index_action) self.srid = srid # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setIconSize(9) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Rubber band self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) mFillColor = QColor(255, 0, 0); self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(3) self.reset() def reset(self): # Clear selected features layer = self.canvas.currentLayer() if layer is not None: layer.removeSelection() # Graphic elements self.rubberBand.reset() def move_node(self, node_id, point): ''' Move selected node to the current point ''' if self.srid is None: self.srid = self.settings.value('db/srid') if self.schema_name is None: self.schema_name = self.settings.value('db/schema_name') # Update node geometry the_geom = "ST_GeomFromText('POINT("+str(point.x())+" "+str(point.y())+")', "+str(self.srid)+")"; sql = "UPDATE "+self.schema_name+".node SET the_geom = "+the_geom sql+= " WHERE node_id = '"+node_id+"'" status = self.controller.execute_sql(sql) if status: # Execute SQL function and show result to the user function_name = "gw_fct_node2arc" sql = "SELECT "+self.schema_name+"."+function_name+"('"+str(node_id)+"');" self.controller.execute_sql(sql) else: message = "Move node: Error updating geometry" self.controller.show_warning(message, context_name='ui_message') # Refresh map canvas self.canvas.currentLayer().triggerRepaint() ''' QgsMapTool inherited event functions ''' def activate(self): ''' Called when set as currently active map tool ''' # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to node self.snapperManager.snapToNode() self.snapperManager.snapToArc() # Change pointer cursor = QCursor() cursor.setShape(Qt.CrossCursor) # Get default cursor self.stdCursor = self.parent().cursor() # And finally we set the mapTool's parent cursor self.parent().setCursor(cursor) # Reset self.reset() # Show help message when action is activated if self.show_help: message = "Select the disconnected node by clicking on it, move the pointer to desired location inside a pipe and click again" self.controller.show_info(message, context_name='ui_message' ) # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_node) except: self.canvas.setCurrentLayer(self.layer_node) def deactivate(self): ''' Called when map tool is being deactivated ''' # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) try: self.rubberBand.reset(QGis.Line) except AttributeError: pass def canvasMoveEvent(self, event): ''' Mouse movement event ''' # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() if layer is None: return # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Set a new point to go on with #self.appendPoint(point) self.rubberBand.movePoint(point) else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) else: # Snap to arc result = [] (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()) and (result[0].snappedVertexNr == -1): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(255, 0, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Select the arc self.layer_arc.removeSelection() self.layer_arc.select([result[0].snappedAtGeometry]) # Bring the rubberband to the cursor i.e. the clicked point self.rubberBand.movePoint(point) else: # Bring the rubberband to the cursor i.e. the clicked point point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, event): ''' Mouse release event ''' if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) layer.select([result[0].snappedAtGeometry]) # Hide highlight self.vertexMarker.hide() # Set a new point to go on with self.rubberBand.addPoint(point) else: # Snap to arc (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()): point = QgsPoint(result[0].snappedVertex) # Get selected feature (at this moment it will have one and only one) feature = layer.selectedFeatures()[0] node_id = feature.attribute('node_id') # Move selected node to the released point self.move_node(node_id, point) # Rubberband reset self.reset() # Refresh map canvas self.iface.mapCanvas().refresh() elif event.button() == Qt.RightButton: self.reset()
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 QgepMapToolAddFeature(QgsMapTool): """ Base class for adding features """ 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): """ When activating the map tool """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) def deactivate(self): """ On deactivating the map tool """ QgsMapTool.deactivate(self) self.canvas.unsetCursor() # pylint: disable=no-self-use def isZoomTool(self): """ This is no zoom tool """ return False # =========================================================================== # Events # =========================================================================== def canvasReleaseEvent(self, event): """ Called when a mouse button is :param event: :return: """ if event.button() == Qt.RightButton: self.rightClicked(event) else: self.leftClicked(event) def leftClicked(self, event): """ When the canvas is left clicked we add a new point to the rubberband. :type event: QMouseEvent """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) self.tempRubberband.reset() def rightClicked(self, _): """ On a right click we create a new feature from the existing rubberband and show the add dialog """ 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 canvasMoveEvent(self, event): """ When the mouse is moved the rubberband needs to be updated :param event: The coordinates etc. """ mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.tempRubberband.movePoint(mousepos)
class MeasureDistanceTool(QgsMapTool): finished = pyqtSignal() def __init__(self, canvas, msglog): super().__init__(canvas) self.canvas = canvas self.msglog = msglog self.start_point = self.end_point = None self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubber_band.setColor(QColor(255, 0, 0, 100)) self.rubber_band.setWidth(3) self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.rubber_band_points.setColor(QColor(255, 0, 0, 150)) crs = self.canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs( crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) self.reset() def reset(self): self.msglog.logMessage("") self.start_point = self.end_point = None self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) def canvasPressEvent(self, event): pass def canvasReleaseEvent(self, event): transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(event.pos().x(), event.pos().y()) if self.start_point: self.end_point = point self.rubber_band.addPoint(self.end_point) self.rubber_band_points.addPoint(self.end_point) distance = self.distance_calc.measureLine( [self.start_point, self.end_point]) bearing = self.distance_calc.bearing(self.start_point, point) distancemsg = QMessageBox(self.parent()) distancemsg.finished.connect(self.deactivate) distancemsg.setWindowTitle("Measure tool") distancemsg.setText("Distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing))) distancemsg.exec() self.finish() else: self.start_point = point self.rubber_band.addPoint(self.start_point) self.rubber_band_points.addPoint(self.start_point) def canvasMoveEvent(self, e): if self.start_point and not self.end_point: transform = self.canvas.getCoordinateTransform() point = transform.toMapCoordinates(e.pos().x(), e.pos().y()) self.rubber_band.movePoint(point) distance = self.distance_calc.measureLine( [self.start_point, point]) bearing = self.distance_calc.bearing(self.start_point, point) self.msglog.logMessage("") self.msglog.logMessage( "Current distance: {:.3F} m. Bearing: {:.3F} º".format( distance, degrees(bearing)), "Measure distance:", 0) def keyPressEvent(self, event): """ When escape key is pressed, line is restarted """ if event.key() == Qt.Key_Escape: self.reset() def finish(self): self.reset() self.finished.emit()
class QGISRedEditLinksGeometryTool(QgsMapTool): ownMainLayers = ["Pipes", "Valves", "Pumps", "ServiceConnections"] def __init__(self, button, iface, projectDirectory, netwName): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.toolbarButton = button self.snapper = None self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.vertexMarker.setColor(QColor(255, 87, 51)) self.vertexMarker.setIconSize(15) self.vertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(3) self.vertexMarker.hide() self.pipeSnapper = None self.pipeMarker = QgsVertexMarker(self.iface.mapCanvas()) self.pipeMarker.setColor(QColor(143, 0, 255)) self.pipeMarker.setIconSize(10) try: self.pipeMarker.setIconType( QgsVertexMarker.ICON_DOUBLE_TRIANGLE) # or ICON_CROSS, ICON_X except: self.pipeMarker.setIconType( QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.pipeMarker.setPenWidth(3) self.pipeMarker.hide() self.mouseClicked = False self.clickedPoint = None self.objectSnapped = None self.pipeSnapped = None self.selectedFeature = None self.selectedLayer = None self.newPositionVector = QgsVector(0, 0) self.rubberBand = None self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.newVertexMarker.setColor(QColor(55, 198, 5)) self.newVertexMarker.setIconSize(15) self.newVertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.newVertexMarker.setPenWidth(3) self.newVertexMarker.hide() def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) myLayers = [] # Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: myLayers.append(layer) if not layer.isEditable(): layer.startEditing() # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(2) # Vertex config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.snapper.setConfig(config) self.pipeSnapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.pipeSnapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(2) # Vertex config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.pipeSnapper.setConfig(config) def deactivate(self): self.toolbarButton.setChecked(False) # End Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: if layer.isModified(): layer.commitChanges() else: layer.rollBack() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def getUniformedPath(self, path): return QGISRedUtils().getUniformedPath(path) def getLayerPath(self, layer): return QGISRedUtils().getLayerPath(layer) def generatePath(self, folder, fileName): return QGISRedUtils().generatePath(folder, fileName) def getLayers(self): return QGISRedUtils().getLayers() def areOverlapedPoints(self, point1, point2): tolerance = 0.1 if point1.distance(point2) < tolerance: return True else: return False def isInPath(self, point1, point2, myPoint): width = point2.x() - point1.x() height = point2.y() - point1.y() widthM = myPoint.x() - point1.x() heightM = myPoint.y() - point1.y() if abs(width) >= abs(height): yEst = widthM * height / width + point1.y() if abs(yEst - myPoint.y()) < 1E-9: return True else: xEst = heightM * width / height + point1.x() if abs(xEst - myPoint.x()) < 1E-9: return True return False def createRubberBand(self, points): myPoints = points if isinstance(points[0], QgsPointXY): myPoints = [] for p in points: myPoints.append(QgsPoint(p.x(), p.y())) self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None) self.rubberBand.setColor(QColor(55, 198, 5)) self.rubberBand.setWidth(1) self.rubberBand.setLineStyle(Qt.DashLine) self.newVertexMarker.setCenter(QgsPointXY(points[0].x(), points[0].y())) self.newVertexMarker.show() def updateRubberBand(self): newX = self.clickedPoint.x() + self.newPositionVector.x() newY = self.clickedPoint.y() + self.newPositionVector.y() self.rubberBand.movePoint(1, QgsPointXY(newX, newY)) self.newVertexMarker.setCenter(QgsPointXY(newX, newY)) def moveVertexLink(self, layer, feature, newPosition, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.moveVertex(newPosition.x(), newPosition.y(), feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def deleteVertexLink(self, layer, feature, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.deleteVertex(feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def insertVertexLink(self, layer, feature, newPoint): if layer.isEditable(): layer.beginEditCommand("Update link geometry") vertex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part for i in range(len(part) - 1): if self.isInPath( QgsPointXY(part[i].x(), part[i].y()), QgsPointXY(part[i + 1].x(), part[i + 1].y()), newPoint): vertex = i + 1 try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.insertVertex(newPoint.x(), newPoint.y(), feature.id(), vertex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() """Events""" def canvasPressEvent(self, event): if self.objectSnapped is None: self.clickedPoint = None return if event.button() == Qt.RightButton: self.mouseClicked = False self.clickedPoint = None if event.button() == Qt.LeftButton: self.clickedPoint = self.objectSnapped.point() if self.vertexIndex == -1: return self.mouseClicked = True self.createRubberBand( [self.objectSnapped.point(), self.objectSnapped.point()]) def canvasMoveEvent(self, event): mousePoint = self.toMapCoordinates(event.pos()) # Mouse not clicked if not self.mouseClicked: self.pipeSnappedOn = False matchSnapper = self.snapper.snapToMap(mousePoint) if matchSnapper.isValid(): valid = False layer = matchSnapper.layer() snapLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if snapLayerPath == layerPath: valid = True if valid: self.objectSnapped = matchSnapper self.selectedLayer = layer vertex = matchSnapper.point() featureId = matchSnapper.featureId() request = QgsFeatureRequest().setFilterFid(featureId) nodes = list(layer.getFeatures(request)) self.selectedFeature = QgsFeature(nodes[0]) # #Ver aquí si es el nudo inicial y final middleNode = False self.vertexIndex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part if middleNode: break i = -1 for v in part: i = i + 1 if ( i == 0 or i == len(part) - 1 ) and "ServiceConnections" not in snapLayerPath: continue matchedPoint = QgsPointXY( vertex.x(), vertex.y()) if self.areOverlapedPoints( QgsGeometry.fromPointXY( matchedPoint), QgsGeometry.fromPointXY( QgsPointXY(v.x(), v.y()))): middleNode = True self.vertexIndex = i if ( i == 0 or i == len(part) - 1 ) and "ServiceConnections" in snapLayerPath: self.pipeSnappedOn = True break if middleNode: self.vertexMarker.setCenter( QgsPointXY(vertex.x(), vertex.y())) self.vertexMarker.show() else: self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() # Mouse clicked else: # Snap pipe layer if self.pipeSnappedOn: matchSnapper = self.pipeSnapper.snapToMap(mousePoint) if matchSnapper.isValid(): valid = False layer = matchSnapper.layer() snapLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_Pipes.shp") if snapLayerPath == layerPath: valid = True if valid: self.pipeSnapped = matchSnapper self.pipeMarker.setCenter(matchSnapper.point()) self.pipeMarker.show() else: self.pipeMarker.hide() else: self.pipeMarker.hide() self.pipeSnapped = None # # Update rubber band if self.objectSnapped is not None and self.rubberBand is not None: snappedPoint = self.objectSnapped.point() self.newPositionVector = QgsVector( mousePoint.x() - snappedPoint.x(), mousePoint.y() - snappedPoint.y()) self.updateRubberBand() def canvasReleaseEvent(self, event): if self.mouseClicked: if event.button() == 1: mousePoint = self.toMapCoordinates(event.pos()) if (self.pipeSnapped is not None): mousePoint = self.pipeSnapped.point() self.mouseClicked = False if self.objectSnapped is not None: self.moveVertexLink(self.selectedLayer, self.selectedFeature, mousePoint, self.vertexIndex) elif event.button() == 2: if self.objectSnapped is not None: self.deleteVertexLink(self.selectedLayer, self.selectedFeature, self.vertexIndex) elif event.button() == 1: if self.objectSnapped is not None: self.insertVertexLink(self.selectedLayer, self.selectedFeature, self.objectSnapped.point()) self.objectSnapped = None self.pipeSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexIndex = -1 self.iface.mapCanvas().refresh() # Remove vertex marker and rubber band self.vertexMarker.hide() self.iface.mapCanvas().scene().removeItem(self.rubberBand) self.newVertexMarker.hide() self.pipeMarker.hide()
class APISMapToolEmitPolygonAndPoint(QgsMapTool, APISMapToolMixin): mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.rubberBand = None self.tempRubberBand = None self.vertexMarker = None self.capturedPoints = [] self.derivedPoint = None self.capturing = False self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.addVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point is not None and polygon is not None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom is not None and polygonGeom is not None: self.mappingFinished.emit( pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def canvasMoveEvent(self, event): if self.tempRubberBand is not None and self.capturing: mapPt = self.transformCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPt) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() event.ignore() if event.key() == Qt.Key_Escape: self.stopCapturing() self.clearScene() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point is not None and polygon is not None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom is not None and polygonGeom is not None: self.mappingFinished.emit( pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def startCapturing(self): self.clearScene() self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rubberBand.setWidth(2) self.rubberBand.setFillColor(QColor(220, 0, 0, 120)) self.rubberBand.setStrokeColor(QColor(220, 0, 0)) self.rubberBand.setLineStyle(Qt.DotLine) self.rubberBand.show() self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.tempRubberBand.setWidth(2) self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0)) self.tempRubberBand.setStrokeColor(QColor(220, 0, 0)) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setIconType(1) self.vertexMarker.setColor(QColor(220, 0, 0)) self.vertexMarker.setIconSize(16) self.vertexMarker.setPenWidth(3) self.vertexMarker.show() self.capturing = True def clearScene(self): if self.vertexMarker: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None def stopCapturing(self): if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices( ) < 3: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.capturing = False self.capturedPoints = [] self.derivedPoint = None self.canvas.refresh() def addVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) self.rubberBand.addPoint(mapPt) self.capturedPoints.append(mapPt) bandSize = self.rubberBand.numberOfVertices() if bandSize > 2: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() self.tempRubberBand.reset(QgsWkbTypes.PolygonGeometry) firstPoint = self.rubberBand.getPoint(0, 0) self.tempRubberBand.addPoint(firstPoint) self.tempRubberBand.movePoint(mapPt) self.tempRubberBand.addPoint(mapPt) def removeLastVertex(self): if not self.capturing: return bandSize = self.rubberBand.numberOfVertices() tempBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if bandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if bandSize > 1: if tempBandSize > 1: point = self.rubberBand.getPoint(0, bandSize - 2) self.tempRubberBand.movePoint(tempBandSize - 2, point) else: self.tempRubberBand.reset(QgsWkbTypes.PolygonGeometry) bandSize = self.rubberBand.numberOfVertices() if bandSize < 3: self.vertexMarker.hide() else: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() del self.capturedPoints[-1] def getCapturedPolygon(self): polygon = self.capturedPoints if len(polygon) < 3: return None else: return polygon def getDerivedPoint(self): point = self.derivedPoint if point is None: return None else: return point def getPointGeometry(self, geom): p = QgsGeometry.fromPointXY(geom) if p.isGeosValid() and not p.isEmpty(): return p else: return None def getPolygonGeometry(self, geom): p = QgsGeometry.fromPolygonXY([geom]) if p.isGeosValid() and not p.isEmpty(): return p else: return None
class LineCreateTool(QgsMapTool): ''' classdocs ''' def __init__(self, canvas, disType = DistanceUnits.M): ''' Constructor ''' self.canvas = canvas QgsMapTool.__init__(self, self.canvas) self.mSnapper = QgsMapCanvasSnapper(self.canvas) self.rubberBand = QgsRubberBand(self.canvas,QGis.Line) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.rubberBandPt = QgsRubberBand(canvas, QGis.Point) self.rubberBandPt.setColor(Qt.red) self.rubberBandPt.setWidth(10) self.type = disType self.reset() def reset(self): self.startPoint = None self.endPoint = None self.isDrawing = False self.distance = 0.0 def canvasReleaseEvent(self, e): if ( e.button() == Qt.RightButton ): self.emit(SIGNAL("resultLineCreate"), self.rubberBand.asGeometry()) self.reset() self.isDrawing = False else: self.rubberBandPt.reset(QGis.Point) snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas, True) if self.startPoint == None: self.rubberBand.reset(QGis.Line) if snapPoint == None: self.startPoint = self.toMapCoordinates(e.pos()) else: self.startPoint = snapPoint self.rubberBand.addPoint(self.startPoint) self.isDrawing = True else: if snapPoint == None: self.endPoint = self.toMapCoordinates(e.pos()) else: self.endPoint = snapPoint self.rubberBand.addPoint(self.endPoint) self.startPoint = self.endPoint def canvasMoveEvent(self, e): self.rubberBandPt.reset(QGis.Point) snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas, True) if snapPoint != None: self.rubberBandPt.addPoint(snapPoint) self.rubberBandPt.show() if self.isDrawing: if self.isDrawing: if snapPoint == None: self.endPoint = self.toMapCoordinates(e.pos()) else: self.endPoint = snapPoint self.rubberBand.movePoint(self.endPoint) def deactivate(self): self.rubberBandPt.reset(QGis.Point) QgsMapTool.deactivate(self) self.emit(SIGNAL("deactivated()"))
class GeomFilterEPSG(): def __init__(self, iface): self.iface = iface def initGui(self): # cria uma ação que iniciará a configuração do plugin #self.myMapTool = QgsMapToolEmitPoint( self.iface.mapCanvas() ) self.initVariables() self.initSignals() def initVariables(self): self.coordinates = [] # Criação da action e da toolbar self.toolbar = self.iface.addToolBar("My_ToolBar") pai = self.iface.mainWindow() icon_path = ':/plugins/GeomFilterEPSG/icon.png' self.action = QAction(QIcon(icon_path), u"Filtro EPSG.", pai) self.action.setObjectName("Filtro EPSG.") self.action.setStatusTip(None) self.action.setWhatsThis(None) self.action.setCheckable(True) self.toolbar.addAction(self.action) self.previousMapTool = self.iface.mapCanvas().mapTool() self.myMapTool = QgsMapToolEmitPoint(self.iface.mapCanvas()) self.isEditing = 0 def initSignals(self): self.action.toggled.connect(self.RubberBand) self.myMapTool.canvasClicked.connect(self.mouseClick) def RubberBand(self, bolean): if bolean: self.myRubberBand = QgsRubberBand(self.iface.mapCanvas(), QGis.Polygon) color = QColor(78, 97, 114) color.setAlpha(190) self.myRubberBand.setColor(color) self.myRubberBand.setFillColor(QColor(255, 0, 0, 40)) self.myRubberBand.setBorderColor(QColor(255, 0, 0, 200)) # Set MapTool self.iface.mapCanvas().setMapTool(self.myMapTool) self.iface.mapCanvas().xyCoordinates.connect(self.mouseMove) else: self.disconnect() def disconnect(self): self.iface.mapCanvas().unsetMapTool(self.myMapTool) try: self.iface.mapCanvas().xyCoordinates.disconnect(self.mouseMove) except: pass try: self.myRubberBand.reset() except: pass def unChecked(self): self.action.setCheckable(False) self.action.setCheckable(True) def unload(self): self.disconnect() def mouseClick(self, currentPos, clickedButton): if clickedButton == Qt.LeftButton: # and myRubberBand.numberOfVertices() == 0: self.myRubberBand.addPoint(QgsPoint(currentPos)) self.coordinates.append(QgsPoint(currentPos)) self.isEditing = 1 elif clickedButton == Qt.RightButton and self.myRubberBand.numberOfVertices( ) > 2: self.isEditing = 0 # create feature and set geometry. poly = QgsFeature() geomP = self.myRubberBand.asGeometry() poly.setGeometry(geomP) g = geomP.exportToWkt() # Get WKT coordenates. #print g canvas = self.iface.mapCanvas() c = canvas.mapRenderer().destinationCrs().authid() # Get EPSG. rep = c.replace("EPSG:", "") string = U"st_intersects(geom,st_geomfromewkt('SRID=" + rep + ";" + g + "'))" self.layers = self.iface.mapCanvas().layers() for layer in self.layers: layer.setSubsetString(string) self.myRubberBand.reset(QGis.Polygon) self.disconnect() self.unChecked() def mouseMove(self, currentPos): if self.isEditing == 1: self.myRubberBand.movePoint(QgsPoint(currentPos))
class QgepMapToolDigitizeDrainageChannel(QgsMapTool): ''' This is used to digitize a drainage channel. It lets you digitize two points and then creates a polygon based on these two points by adding an orthogonal offset at each side. Input: x==============x Output: ---------------- | | ---------------- Usage: Connect to the signals deactivated() and geometryDigitized() If geometryDigitized() is called you can use the member variable geometry which will contain a rectangle polygon deactivated() will be emited after a right click ''' geometryDigitized = pyqtSignal() 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(), QGis.Line) self.rubberband.setColor(QColor("#ee5555")) self.rubberband.setWidth(2) self.firstPoint = None self.messageBarItem = None self.geometry = None def activate(self): """ Map tool is activated """ QgsMapTool.activate(self) self.canvas.setCursor(QCursor(Qt.CrossCursor)) msgtitle = self.tr('Digitizing Drainage Channel') msg = self.tr('Digitize start and end point. Rightclick to abort.') self.messageBarItem = QgsMessageBar.createMessage(msgtitle, msg) self.iface.messageBar().pushItem(self.messageBarItem) def deactivate(self): """ Map tool is deactivated """ QgsMapTool.deactivate(self) self.iface.messageBar().popWidget(self.messageBarItem) try: self.iface.mapCanvas().scene().removeItem(self.rubberband) del self.rubberband except AttributeError: # Called repeatedly... bail out pass self.canvas.unsetCursor() def canvasMoveEvent(self, event): """ Mouse is moved: Update rubberband :param event: coordinates etc. """ mousepos = event.mapPoint() self.rubberband.movePoint(mousepos) def canvasReleaseEvent(self, event): """ Canvas is released. This means: * start digitizing * stop digitizing (create a rectangle * if the Ctrl-modifier is pressed, ask for the rectangle width :param event: coordinates etc. """ if event.button() == Qt.RightButton: self.deactivate() else: mousepos = self.canvas.getCoordinateTransform()\ .toMapCoordinates(event.pos().x(), event.pos().y()) self.rubberband.addPoint(mousepos) if self.firstPoint: # If the first point was set before, we are doing the second one lp1 = self.rubberband.asGeometry().asPolyline()[0] lp2 = self.rubberband.asGeometry().asPolyline()[1] width = 0.2 if QApplication.keyboardModifiers() & Qt.ControlModifier: dlg = QDialog() dlg.setLayout(QGridLayout()) dlg.layout().addWidget(QLabel(self.tr('Enter width'))) txt = QLineEdit('0.2') dlg.layout().addWidget(txt) bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) dlg.layout().addWidget(bb) bb.accepted.connect(dlg.accept) bb.rejected.connect(dlg.reject) if dlg.exec_(): try: width = float(txt.text()) except ValueError: width = 0.2 length = math.sqrt(math.pow(lp1.x() - lp2.x(), 2) + math.pow(lp1.y() - lp2.y(), 2)) xd = lp2.x() - lp1.x() yd = lp2.y() - lp1.y() pt1 = QgsPoint(lp1.x() + width * (yd / length), lp1.y() - width * (xd / length)) pt2 = QgsPoint(lp1.x() - width * (yd / length), lp1.y() + width * (xd / length)) pt3 = QgsPoint(lp2.x() - width * (yd / length), lp2.y() + width * (xd / length)) pt4 = QgsPoint(lp2.x() + width * (yd / length), lp2.y() - width * (xd / length)) self.geometry = QgsGeometry.fromPolygon([[pt1, pt2, pt3, pt4, pt1]]) self.geometryDigitized.emit() self.firstPoint = mousepos
class EditTool(QgsMapTool): wp_clicked = pyqtSignal(int) def __init__(self, mission_track, canvas, msglog): QgsMapTool.__init__(self, canvas) self.setCursor(Qt.CrossCursor) self.mission_track = mission_track self.msglog = msglog self.dragging = False self.feature = None self.vertex = None self.startcoord = None self.layer = self.mission_track.get_mission_layer() logger.info(self.mission_track.get_mission_name()) self.rubber_band = QgsRubberBand(self.canvas(), QgsWkbTypes.LineGeometry) self.rubber_band.setWidth(2) self.rubber_band.setColor(QColor("green")) self.point_cursor_band = QgsRubberBand(self.canvas(), QgsWkbTypes.LineGeometry) self.point_cursor_band.setWidth(1) self.point_cursor_band.setLineStyle(Qt.DashLine) self.point_cursor_band.setColor(QColor(255, 0, 0, 100)) self.mid_point_band = QgsRubberBand(self.canvas(), QgsWkbTypes.PointGeometry) self.mid_point_band.setColor(QColor(255, 0, 0, 100)) self.mid_point_band.setIconSize(18) self.rubber_band_points = QgsRubberBand(self.canvas(), QgsWkbTypes.PointGeometry) self.rubber_band_points.setColor(QColor("green")) self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE) self.rubber_band_points.setIconSize(10) self.mission_track.mission_changed.connect(self.update_rubber_bands) self.vertex_marker = QgsVertexMarker(self.canvas()) self.start_end_marker = StartEndMarker(canvas, self.mission_track.find_waypoints_in_mission(), QColor("green")) self.layer.startEditing() self.wp = [] self.mCtrl = False # handler for mission feature self.update_rubber_bands(0) crs = canvas.mapSettings().destinationCrs() self.distance_calc = QgsDistanceArea() self.distance_calc.setSourceCrs(crs, QgsProject.instance().transformContext()) self.distance_calc.setEllipsoid(crs.ellipsoidAcronym()) def update_rubber_bands(self, current_wp): self.rubber_band.reset(QgsWkbTypes.LineGeometry) self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.wp = self.mission_track.find_waypoints_in_mission() self.start_end_marker.update_markers(self.wp) if len(self.wp) > 0: for v in self.wp: pc = self.toLayerCoordinates(self.layer, QgsPointXY(v)) self.rubber_band.addPoint(pc) self.rubber_band_points.addPoint(pc) logger.debug("MISSION UPDATE: now we have {} waypoints".format(len(self.wp))) self.vertex_marker.setCenter(QgsPointXY(self.wp[current_wp].x(), self.wp[current_wp].y())) self.vertex_marker.setColor(QColor(25, 255, 0)) self.vertex_marker.setIconSize(7) self.vertex_marker.setIconType(QgsVertexMarker.ICON_X) # ICON_BOX, ICON_CROSS, ICON_X self.vertex_marker.setPenWidth(2) self.vertex_marker.show() self.set_geometry() else: self.vertex_marker.hide() def set_control_state(self, state): self.mCtrl = state def keyPressEvent(self, event): if event.key() == Qt.Key_Control and not self.dragging: self.mCtrl = True pos = self.canvas().mouseLastXY() if not self.find_on_feature(pos, self.calc_tolerance()): self.show_dist_and_bearing_to_point() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = False pos = self.canvas().mouseLastXY() if not self.find_on_feature(pos, self.calc_tolerance()) and not self.dragging: self.show_dist_and_bearing_to_point() return def canvasDoubleClickEvent(self, event): self.canvasPressEvent(event) def canvasPressEvent(self, event): if self.dragging: self.canvasReleaseEvent(event) map_pt, layer_pt = self.transform_coordinates(event.pos()) tolerance = self.calc_tolerance() if not self.find_on_feature(event.pos(), tolerance): if event.button() == Qt.LeftButton: # we have clicked outside the track logger.debug("We have clicked outside the track") self.point_cursor_band.reset(QgsWkbTypes.LineGeometry) if not self.mCtrl: # add step to mission at the end self.mission_track.add_step(len(self.wp), layer_pt) self.show_waypoint_distances(len(self.wp)-1) else: self.mission_track.add_step(0, layer_pt) self.show_waypoint_distances(0) else: logger.debug("We have clicked on the track") vertex = self.find_vertex_at(event.pos(), tolerance) if event.button() == Qt.LeftButton: if vertex is None: logger.debug("We have clicked between vertexs") # we have clicked in between vertex, add intermediate point initial_vertex = self.find_segment_at(event.pos()) # self.mission_track.add_step(initial_vertex + 1, layerPt) intersection = intersect_point_to_line(self.toLayerCoordinates(self.layer, event.pos()), QgsPointXY(self.wp[initial_vertex]), QgsPointXY(self.wp[initial_vertex + 1])) logger.debug("intersection point: {} {}".format(str(intersection.x()), str(intersection.y()))) logger.debug("{} {} {} {}".format(self.wp[initial_vertex].x(), self.wp[initial_vertex].y(), self.wp[initial_vertex + 1].x(), self.wp[initial_vertex + 1].y())) # layerPtIntersection = self.toLayerCoordinates(self.layer,intersection) self.mission_track.add_step(initial_vertex + 1, intersection) self.mid_point_band.reset(QgsWkbTypes.PointGeometry) self.show_waypoint_distances(initial_vertex+1) else: logger.debug("We have clicked on vertex {}".format(vertex)) # we have clicked on a vertex # Left click -> move vertex. self.dragging = True self.vertex = vertex self.startcoord = event.pos() # self.moveVertexTo(layerPt) elif event.button() == Qt.RightButton: if vertex is not None and not self.dragging: # Right click -> delete vertex. self.delete_vertex(vertex) if self.find_on_feature(event.pos(), tolerance): # If cursor still over track vertex = self.find_vertex_at(event.pos(), tolerance) if vertex is None: # Cursor is between vertexes self.show_mid_point(event.pos()) else: # Cursor is over a vertex self.show_waypoint_distances(vertex) else: self.show_dist_and_bearing_to_point() def transform_coordinates(self, canvas_pt): return (self.toMapCoordinates(canvas_pt), self.toLayerCoordinates(self.layer, canvas_pt)) def canvasMoveEvent(self, event): if self.dragging: self.move_vertex_to(self.toLayerCoordinates(self.layer, event.pos())) self.mission_track.hide_start_end_markers() self.vertex_marker.hide() self.start_end_marker.hide_markers() self.show_waypoint_distances(self.vertex) else: tolerance = self.calc_tolerance() if self.find_on_feature(event.pos(), tolerance): # if mouse is over the track self.point_cursor_band.reset(QgsWkbTypes.LineGeometry) self.mid_point_band.reset(QgsWkbTypes.PointGeometry) vertex = self.find_vertex_at(event.pos(), tolerance) if vertex is None: # Cursor is between vertexes self.show_mid_point(event.pos()) else: # Cursor is over a vertex self.show_waypoint_distances(vertex) else: self.mid_point_band.reset(QgsWkbTypes.PointGeometry) self.show_dist_and_bearing_to_point() def show_dist_and_bearing_to_point(self): """ Finds distance and bearing from the last point (first if pressing ctrl) to the specified point and shows them in the message log. Also draws a line between the points. """ bearing = 0.0 self.point_cursor_band.reset(QgsWkbTypes.LineGeometry) point = self.canvas().mouseLastXY() if len(self.wp) > 0: cursor_point = self.toMapCoordinates(point) if self.mCtrl: anchor_point = QgsPointXY(self.wp[0]) else: anchor_point = QgsPointXY(self.wp[len(self.wp) - 1]) self.point_cursor_band.addPoint(cursor_point) self.point_cursor_band.addPoint(anchor_point) distance = self.distance_calc.measureLine([anchor_point, cursor_point]) if distance != 0.0: bearing = self.distance_calc.bearing(anchor_point, cursor_point) self.msglog.logMessage("") if self.mCtrl: msg = "Distance to next point: " else: msg = "Distance to previous point: " self.msglog.logMessage(msg + "{:.3F} m. Bearing: {:.3F} º.".format(distance, math.degrees(bearing)), "Distance and bearing", 0) else: self.msglog.logMessage("") def show_mid_point(self, cursor): """ Finds the projection of the cursor over the track and draws a circle in that point. Finds the distances between this projection point and the previous and next points in the mission and shows them in the message log. :param cursor: position to be projected over the track """ prev_vertex = self.find_segment_at(cursor) prev_point = QgsPointXY(self.wp[prev_vertex]) next_point = QgsPointXY(self.wp[prev_vertex + 1]) cursor_point = self.toMapCoordinates(cursor) intersection = intersect_point_to_line(cursor_point, prev_point, next_point) self.mid_point_band.addPoint(intersection) distance1 = self.distance_calc.measureLine([prev_point, intersection]) distance2 = self.distance_calc.measureLine([intersection, next_point]) self.msglog.logMessage("") self.msglog.logMessage("Distance to previous point: {:.3F} m. Distance to next point: {:.3F} m." .format(distance1, distance2), "Distance between points", 0) def show_waypoint_distances(self, vertex): """ Finds the distances to adjacent waypoints of vertex and shows them in the message log :param vertex: index of the waypoint from the mission """ curr_point = self.rubber_band_points.getPoint(QgsWkbTypes.PointGeometry, vertex) if vertex == 0: if len(self.wp) > 1: next_point = QgsPointXY(self.wp[vertex+1]) distance = self.distance_calc.measureLine([curr_point, next_point]) bearing = self.distance_calc.bearing(next_point, curr_point) msg = "Distance to next point: {:.3F} m. Bearing: {:.3F} º.".format(distance, math.degrees(bearing)) else: msg = "" self.msglog.logMessage("") self.msglog.logMessage(msg+" (Waypoint {}) ".format(vertex+1), "Vertex distances", 0) elif vertex == len(self.wp) - 1: prev_point = QgsPointXY(self.wp[vertex-1]) distance = self.distance_calc.measureLine([prev_point, curr_point]) bearing = self.distance_calc.bearing(prev_point, curr_point) msg = "Distance to previous point: {:.3F} m. Bearing: {:.3F} º.".format(distance, math.degrees(bearing)) self.msglog.logMessage("") self.msglog.logMessage(msg+" (Waypoint {})".format(vertex+1), "Vertex distances", 0) else: prev_point = QgsPointXY(self.wp[vertex-1]) next_point = QgsPointXY(self.wp[vertex+1]) distance1 = self.distance_calc.measureLine(prev_point, curr_point) distance2 = self.distance_calc.measureLine(curr_point, next_point) msg = "Distance to previous point: {:.3F} m. Distance to next point: {:.3F} m."\ .format(distance1, distance2) self.msglog.logMessage("") self.msglog.logMessage(msg+" (Waypoint {})".format(vertex+1), "Vertex distances", 0) def hide_point_cursor_band(self): """ Hides the rubber band drawn from last (or first) point to cursor """ self.point_cursor_band.reset(QgsWkbTypes.LineGeometry) def canvasReleaseEvent(self, event): if self.dragging and event.button() == Qt.LeftButton: self.dragging = False self.vertex_marker.show() mapPt, layerPt = self.transform_coordinates(event.pos()) # Check distance with initial point dist = math.sqrt( (self.startcoord.x() - event.pos().x()) ** 2 + (self.startcoord.y() - event.pos().y()) ** 2) tolerance = self.calc_tolerance() if dist > tolerance: self.move_vertex_to(layerPt) self.mission_track.change_position(self.vertex, layerPt) self.wp_clicked.emit(self.vertex) self.feature = None self.vertex = None self.layer.updateExtents() else: # If release point is the same, has been just a click self.move_vertex_to(self.toLayerCoordinates(self.layer, QgsPointXY(self.wp[self.vertex]))) self.wp_clicked.emit(self.vertex) self.feature = None self.vertex = None def calc_tolerance(self): """ Compute the tolerance on canvas :return: tolerance """ # 2% of tolerance width_tolerance = 0.02 * self.canvas().width() height_tolerance = 0.02 * self.canvas().height() if width_tolerance < height_tolerance: tolerance = width_tolerance else: tolerance = height_tolerance return tolerance def move_vertex_to(self, layerPt): """ Move current vertex to layerPt position. :param layerPt: layer point :type: QgsPointXY """ if len(self.wp) > 1: self.rubber_band.movePoint(self.vertex, layerPt) self.rubber_band_points.movePoint(self.vertex, layerPt) elif len(self.wp) == 1: # A rubber band with PointGeometry and only 1 point acts as if it had 2 points, we need to reset it in # order to move the point. self.rubber_band_points.reset(QgsWkbTypes.PointGeometry) self.rubber_band_points.addPoint(layerPt) def delete_vertex(self, vertex): """ Delete step 'vertex'. :param vertex: step """ self.mission_track.remove_step(vertex) self.dragging = False self.vertex = None def find_on_feature(self, pos, tolerance): """ if clicked point has some segment at a smaller distance than tolerance means that we've clicked on the track :param pos: The point that we've clicked :param tolerance: The tolerance of pos :return: bool """ if len(self.wp) > 1: dist_to_segment = [] for v in range(0, len(self.wp) - 1): # convert layer coordinates to canvas coordinates a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v])) b1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v + 1])) dist_to_segment.append( self.dist_to_segment(a1.x(), a1.y(), b1.x(), b1.y(), pos.x(), pos.y())) logger.debug("dist to segment: {}".format(dist_to_segment)) if dist_to_segment[v] < tolerance: return True return False else: # last waypoint vertex = self.find_vertex_at(pos, tolerance) if vertex is None: return False else: return True def find_segment_at(self, pos): """ get the segment that is closer to the clicked point and return its initial vertex :param pos: the point that we've clicked :return: initial vertex of the segment """ dist_to_segment = [] for v in range(0, len(self.wp) - 1): a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v])) b1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v + 1])) dist_to_segment.append(self.dist_to_segment(a1.x(), a1.y(), b1.x(), b1.y(), pos.x(), pos.y())) vertex = dist_to_segment.index(min(dist_to_segment)) return vertex def find_vertex_at(self, pos, tolerance): """ get the vertex that is closer to the clicked point :param pos: The point that we've clicked :param tolerance: The tolerance of pos :return: vertex or None """ if len(self.wp) > 0: dist_to_vertex = [] logger.debug("tolerance {}".format(tolerance)) for v in range(0, len(self.wp)): a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v])) dist_to_vertex.append(math.sqrt((pos.x() - a1.x()) ** 2 + (pos.y() - a1.y()) ** 2)) logger.debug("dist to vertex: {}".format(dist_to_vertex)) vertex = dist_to_vertex.index(min(dist_to_vertex)) if min(dist_to_vertex) > tolerance: return None else: logger.debug("ON VERTEX") return vertex else: return None def dist_to_segment(self, ax, ay, bx, by, cx, cy): """ Computes the minimum distance between a point (cx, cy) and a line segment with endpoints (ax, ay) and (bx, by). :param ax: endpoint 1, x-coordinate :param ay: endpoint 1, y-coordinate :param bx: endpoint 2, x-coordinate :param by: endpoint 2, y-coordinate :param cx: point, x-coordinate :param cy: point, x-coordinate :return: minimum distance between point and line segment """ # calculate tolerance tolerance = self.calc_tolerance() # get distance between points c-a and c-b dist_to_a = math.sqrt((cx - ax) ** 2 + (cy - ay) ** 2) dist_to_b = math.sqrt((cx - bx) ** 2 + (cy - by) ** 2) # if distance to point a or distance to point b is smaller than tolerance, return -1 if (dist_to_a < tolerance) or (dist_to_b < tolerance): return -1 # if one coordinate are between a coordinates or b coordinates if is_between(ax, ay, bx, by, cx, cy): y = (by - ay) x = (bx - ax) # line defined by two points formula num = abs((y * cx) - (x * cy) + (bx * ay) - (by * ax)) den = math.sqrt(y ** 2 + x ** 2) dl = num / den return dl else: return tolerance + 1 def set_geometry(self): """ Save rubber band to geometry of the layer """ if self.layer.featureCount() == 0: # no feature yet created f = QgsFeature() if len(self.wp) == 1: f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(self.wp[0].x(), self.wp[0].y()))) else: f.setGeometry(QgsGeometry.fromPolyline(self.wp)) # self.layer.dataProvider().addFeatures([f]) self.layer.addFeatures([f]) else: # mission feature present, edit geometry feats = self.layer.getFeatures() for f in feats: if len(self.wp) == 1: self.layer.changeGeometry(f.id(), QgsGeometry.fromPointXY(QgsPointXY(self.wp[0].x(), self.wp[0].y()))) else: self.layer.changeGeometry(f.id(), QgsGeometry.fromPolyline(self.wp)) self.layer.commitChanges() self.layer.startEditing() def close_band(self): self.start_end_marker.close_markers() self.vertex_marker.hide() self.canvas().scene().removeItem(self.vertex_marker) self.vertex_marker = None self.mission_track.mission_changed.disconnect() self.layer.commitChanges() self.rubber_band.hide() self.mid_point_band.hide() self.rubber_band_points.hide() self.point_cursor_band.hide() self.canvas().scene().removeItem(self.rubber_band) self.canvas().scene().removeItem(self.mid_point_band) self.canvas().scene().removeItem(self.rubber_band_points) self.canvas().scene().removeItem(self.point_cursor_band) self.rubber_band = None self.mid_point_band = None self.rubber_band_points = None self.point_cursor_band = None self.msglog.logMessage("")
class ALKISPolygonInfo(QgsMapTool): def __init__(self, plugin): QgsMapTool.__init__(self, plugin.iface.mapCanvas()) self.plugin = plugin self.iface = plugin.iface self.cursor = QCursor(QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), self.plugin.PolygonGeometry) self.areaMarkerLayer = None def canvasPressEvent(self, e): if self.areaMarkerLayer is None: (layerId, ok) = QgsProject.instance().readEntry("alkis", "/areaMarkerLayer") if ok: self.areaMarkerLayer = self.plugin.mapLayer(layerId) if self.areaMarkerLayer is None: QMessageBox.warning(None, "ALKIS", u"Fehler: Flächenmarkierungslayer nicht gefunden!") def canvasMoveEvent(self, e): if self.rubberBand.numberOfVertices() > 0: point = self.iface.mapCanvas().getCoordinateTransform().toMapCoordinates(e.x(), e.y()) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, e): point = self.iface.mapCanvas().getCoordinateTransform().toMapCoordinates(e.x(), e.y()) if e.button() == Qt.LeftButton: self.rubberBand.addPoint(point) return QApplication.setOverrideCursor(Qt.WaitCursor) if self.rubberBand.numberOfVertices() >= 3: g = self.plugin.transform( self.rubberBand.asGeometry() ) self.rubberBand.reset(self.plugin.PolygonGeometry) fs = self.plugin.highlight( where=u"st_intersects(wkb_geometry,st_geomfromtext('POLYGON((%s))'::text,%d))" % ( ",".join(["%.3lf %.3lf" % (p[0], p[1]) for p in g.asPolygon()[0]]), self.plugin.getepsg() ) ) if len(fs) == 0: QApplication.restoreOverrideCursor() QMessageBox.information(None, u"Fehler", u"Keine Flurstücke gefunden.") return gmlids = [] for e in fs: gmlids.append(e['gmlid']) try: s = QSettings("norBIT", "EDBSgen/PRO") for i in range(0, len(fs)): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("localhost", int(s.value("norGISPort", "6102")))) sock.send("NORGIS_MAIN#EDBS#ALBKEY#{}#{}#".format(fs[i]['flsnr'], 0 if i + 1 == len(fs) else 1).encode("utf-8")) sock.close() if win32: window = win32gui.FindWindow(None, s.value("albWin", "norGIS")) win32gui.SetForegroundWindow(window) except socket.error: QMessageBox.information(None, u"Fehler", u"Verbindung zu norGIS schlug fehl.") else: self.rubberBand.reset(self.plugin.PolygonGeometry) QApplication.restoreOverrideCursor()
class QgsMapToolCaptureSpatialOperand (QgsMapTool): """ QGIS map tool. Draw a point, line, polygon or bounding box and convert it to WKT format. """ selectionFinished = QtCore.pyqtSignal(str) def __init__(self, canvas, gmlOperand = "gml:Envelope", srsName="", parent = None): QgsMapTool.__init__ (self, canvas) self.canvas = canvas self.parent = parent if gmlOperand == "gml:Point": self.minPoints = 1 self.maxPoints = 1 self.isPolygon = False elif gmlOperand == "gml:Envelope": self.minPoints = 2 self.maxPoints = 2 self.isPolygon = True elif gmlOperand == "gml:Polygon": self.minPoints = 3 self.maxPoints = 0 self.isPolygon = True elif gmlOperand == "gml:LineString": self.minPoints = 2 self.maxPoints = 0 self.isPolygon = False else: pass self.srsName = srsName self.rect = QtCore.QRect() if self.isPolygon and self.maxPoints == 2 else None if self.isPolygon: self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) else: self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) self.rubberBand.setColor(QtGui.QColor (255, 0,0, 150)) self.rubberBand.setWidth(1) self.cursor = QtGui.QCursor (QtCore.Qt.CrossCursor) self.vertexMarkers = [] self.captureList = [] self.crs = QgsCoordinateReferenceSystem() self.crs.createFromUserInput(self.srsName) self.yx = self.crs.axisInverted() def canvasPressEvent(self, event): pass def canvasMoveEvent(self, event): if isinstance (self.rect, QtCore.QRect): self.rect.setBottomRight(event.pos()) self.moveVertex(self.toMapCoordinates(event.pos())) def canvasReleaseEvent(self, event): numPoints = self.addVertex(self.toMapCoordinates(event.pos())) if numPoints == 1 and isinstance (self.rect, QtCore.QRect): self.rect.setTopLeft(event.pos()) if (event.button() == QtCore.Qt.RightButton and numPoints >= self.minPoints) or \ (numPoints == self.maxPoints): self.finishGeom (numPoints) def keyReleaseEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.selectionFinished.emit(None) def activate(self): QgsMapTool.activate(self) self.canvas.setCursor(self.cursor) def deactivate(self): QgsMapTool.deactivate(self) self.canvas.unsetCursor() self.clearMapCanvas() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return False def addVertex(self, pt): self.rubberBand.addPoint(pt) m = QgsVertexMarker(self.canvas) m.setCenter(pt) self.vertexMarkers.append(m) #if self.yx: pt = QgsPoint(pt.y(),pt.x()) self.captureList.append (pt) return len(self.captureList) def moveVertex(self, pt): if self.rubberBand.numberOfVertices() > 1: if isinstance (self.rect, QtCore.QRect): transform = self.canvas.getCoordinateTransform() ll = transform.toMapCoordinates( self.rect.left(), self.rect.bottom() ); ur = transform.toMapCoordinates( self.rect.right(), self.rect.top() ); self.rubberBand.reset(QGis.Polygon) self.rubberBand.addPoint( ll, False ) self.rubberBand.addPoint( QgsPoint( ur.x(), ll.y() ), False ) self.rubberBand.addPoint( ur, False ) self.rubberBand.addPoint( QgsPoint( ll.x(), ur.y() ), True ) else: self.rubberBand.movePoint(pt) def finishGeom (self, numPoints): if self.maxPoints == 1: geom = QgsGeometry.fromPoint(self.captureList[0]) elif self.isPolygon and numPoints == 2: geom = QgsGeometry.fromPolyline(self.captureList) #geom = QgsGeometry.fromRect(geom.boundingBox()) elif self.isPolygon: geom = QgsGeometry.fromPolygon([self.captureList]) else: geom = QgsGeometry.fromPolyline(self.captureList) geom.simplify(0.00001) geom.transform(QgsCoordinateTransform( self.canvas.mapSettings().destinationCrs(), self.crs)) if self.yx: i = 0 vertex = geom.vertexAt(i) while (vertex != QgsPoint(0,0)): x = vertex.x() y = vertex.y() geom.moveVertex(y, x, i) i+=1 vertex = geom.vertexAt(i) self.selectionFinished.emit(geom.exportToWkt()) self.clearMapCanvas() def clearMapCanvas(self): """ Clears the map canvas and in particular the rubberband. A warning is thrown when the markers are removed. """ # Reset the capture list self.captureList = [] # Create an empty rubber band if self.isPolygon: self.rubberBand.reset(QGis.Polygon) else: self.rubberBand.reset(QGis.Line) # Delete also all vertex markers for marker in self.vertexMarkers: self.canvas.scene().removeItem(marker) del marker self.canvas.refresh()
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. """ # A dict of layers # and for each layer the fields to use as foreign key # as well as the possible target layers # Reaches can be connected to reaches and nodes # Catchment areas only to nodes self.network_element_sources = { QgepLayerManager.layer('vw_qgep_reach'): { 'fields': [ ('rp_to_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')), ('rp_from_fk_wastewater_networkelement', QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From')) ], 'target_layers': [ QgepLayerManager.layer('vw_wastewater_node'), QgepLayerManager.layer('vw_qgep_reach') ]}, QgepLayerManager.layer('od_catchment_area'): {'fields': [ ('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')) ], 'target_layers': [ QgepLayerManager.layer('vw_wastewater_node') ]} } self.setSnapLayers(self.source_snapper, self.network_element_sources.keys()) self.reset() self.action.setChecked(True) self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor)) def setSnapLayers(self, snapper, layers): snap_layers = list() for layer in layers: if layer: snap_layer = QgsSnappingUtils.LayerConfig( layer, QgsPointLocator.All, 16, QgsTolerance.Pixels) snap_layers.append(snap_layer) snapper.setLayers(snap_layers) snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced) 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: # There is already a source feature : snap to target feature # candidates 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: # Snapped to source feature, update source feature rubber band # and target layer snapper 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.setSnapLayers(self.target_snapper, self.network_element_sources[ snap_match.layer()]['target_layers']) 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()]['fields']: 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 SplitMapTool(QgsMapToolEdit): def __init__(self, canvas, layer, actionMoveVertices, actionAddVertices, actionRemoveVertices, actionMoveSegment, actionLineClose, actionLineOpen, actionMoveLine): super(SplitMapTool, self).__init__(canvas) self.canvas = canvas self.scene = canvas.scene() self.layer = layer self.actionMoveVertices = actionMoveVertices self.actionAddVertices = actionAddVertices self.actionRemoveVertices = actionRemoveVertices self.actionMoveSegment = actionMoveSegment self.actionLineClose = actionLineClose self.actionLineOpen = actionLineOpen self.actionMoveLine = actionMoveLine self.initialize() def initialize(self): try: self.canvas.renderStarting.disconnect(self.mapCanvasChanged) except: pass self.canvas.renderStarting.connect(self.mapCanvasChanged) try: self.layer.editingStopped.disconnect(self.stopCapturing) except: pass self.layer.editingStopped.connect(self.stopCapturing) self.selectedFeatures = self.layer.selectedFeatures() self.rubberBand = None self.tempRubberBand = None self.capturedPoints = [] self.capturing = False self.setCursor(Qt.CrossCursor) self.proj = QgsProject.instance() self.labels = [] self.vertices = [] self.calculator = QgsDistanceArea() self.calculator.setSourceCrs(self.layer.dataProvider().crs(), QgsProject.instance().transformContext()) self.calculator.setEllipsoid( self.layer.dataProvider().crs().ellipsoidAcronym()) self.drawingLine = False self.movingVertices = False self.addingVertices = False self.removingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.lineClosed = False def restoreAction(self): self.addingVertices = False self.removingVertices = False self.movingVertices = False self.movingSegment = False self.movingLine = False self.showingVertices = False self.drawingLine = True self.movingVertex = -1 self.movingLineInitialPoint = None self.deleteVertices() self.redrawRubberBand() self.redrawTempRubberBand() self.canvas.scene().addItem(self.tempRubberBand) self.redrawActions() def mapCanvasChanged(self): self.redrawAreas() if self.showingVertices: self.redrawVertices() def canvasMoveEvent(self, event): if self.drawingLine and not self.lineClosed: if self.tempRubberBand != None and self.capturing: mapPoint = self.toMapCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPoint) self.redrawAreas(event.pos()) if self.movingVertices and self.movingVertex >= 0: layerPoint = self.toLayerCoordinates(self.layer, event.pos()) self.capturedPoints[self.movingVertex] = layerPoint if self.lineClosed and self.movingVertex == 0: self.capturedPoints[len(self.capturedPoints) - 1] = layerPoint self.redrawRubberBand() self.redrawVertices() self.redrawAreas() if self.movingSegment and self.movingSegm >= 0: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) self.capturedPoints[self.movingSegm] = self.projectPoint( self.capturedPoints[self.movingSegm], distance, bearing) self.capturedPoints[self.movingSegm + 1] = self.projectPoint( self.capturedPoints[self.movingSegm + 1], distance, bearing) if self.lineClosed: if self.movingSegm == 0: self.capturedPoints[ len(self.capturedPoints) - 1] = self.projectPoint( self.capturedPoints[len(self.capturedPoints) - 1], distance, bearing) elif self.movingSegm == len(self.capturedPoints) - 2: self.capturedPoints[0] = self.projectPoint( self.capturedPoints[0], distance, bearing) self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.movingLineInitialPoint = currentPoint if self.movingLine and self.movingLineInitialPoint != None: currentPoint = self.toLayerCoordinates(self.layer, event.pos()) distance = self.distancePoint(currentPoint, self.movingLineInitialPoint) bearing = self.movingLineInitialPoint.azimuth(currentPoint) for i in range(len(self.capturedPoints)): self.capturedPoints[i] = self.projectPoint( self.capturedPoints[i], distance, bearing) self.redrawRubberBand() self.redrawAreas() self.movingLineInitialPoint = currentPoint def projectPoint(self, point, distance, bearing): rads = bearing * pi / 180.0 dx = distance * sin(rads) dy = distance * cos(rads) return QgsPointXY(point.x() + dx, point.y() + dy) def redrawAreas(self, mousePos=None): self.deleteLabels() if self.capturing and len(self.capturedPoints) > 0: for i in range(len(self.selectedFeatures)): geometry = QgsGeometry(self.selectedFeatures[i].geometry()) movingPoints = list(self.capturedPoints) if mousePos != None: movingPoints.append( self.toLayerCoordinates(self.layer, mousePos)) result, newGeometries, topoTestPoints = geometry.splitGeometry( movingPoints, self.proj.topologicalEditing()) self.addLabel(geometry) if newGeometries != None and len(newGeometries) > 0: for i in range(len(newGeometries)): self.addLabel(newGeometries[i]) def addLabel(self, geometry): area = self.calculator.measureArea(geometry) labelPoint = geometry.pointOnSurface().vertexAt(0) label = QGraphicsTextItem("%.2f" % round(area, 2)) label.setHtml( "<div style=\"color:#ffffff;background:#111111;padding:5px\">" + "%.2f" % round(area, 2) + " " + areaUnits[self.calculator.areaUnits()] + "</div>") point = self.toMapCoordinatesV2(self.layer, labelPoint) label.setPos(self.toCanvasCoordinates(QgsPointXY(point.x(), point.y()))) self.scene.addItem(label) self.labels.append(label) def deleteLabels(self): for i in range(len(self.labels)): self.scene.removeItem(self.labels[i]) self.labels = [] def canvasPressEvent(self, event): if self.movingVertices: for i in range(len(self.capturedPoints)): point = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(point.x(), point.y())) if self.distancePoint(event.pos(), currentVertex) <= maxDistanceHitTest: self.movingVertex = i break if self.movingSegment: for i in range(len(self.capturedPoints) - 1): vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) if self.distancePointLine( event.pos().x(), event.pos().y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) <= maxDistanceHitTest: self.movingSegm = i break self.movingLineInitialPoint = self.toLayerCoordinates( self.layer, event.pos()) def distancePoint(self, eventPos, vertexPos): return sqrt((eventPos.x() - vertexPos.x())**2 + (eventPos.y() - vertexPos.y())**2) def canvasReleaseEvent(self, event): if self.movingVertices or self.movingSegment or self.movingLine: if event.button() == Qt.RightButton: self.finishOperation() elif self.addingVertices: if event.button() == Qt.LeftButton: self.addVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() elif self.removingVertices: if event.button() == Qt.LeftButton: self.removeVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() else: if event.button() == Qt.LeftButton: if not self.lineClosed: if not self.capturing: self.startCapturing() self.addEndingVertex(event.pos()) elif event.button() == Qt.RightButton: self.finishOperation() self.movingVertex = -1 self.movingSegm = -1 self.movingLineInitialPoint = None self.redrawActions() def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.stopCapturing() if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: self.finishOperation() event.accept() self.redrawActions() def finishOperation(self): self.doSplit() self.stopCapturing() self.initialize() self.startCapturing() def doSplit(self): if self.capturedPoints != None: self.layer.splitFeatures(self.capturedPoints, self.proj.topologicalEditing()) def startCapturing(self): self.prepareRubberBand() self.prepareTempRubberBand() self.drawingLine = True self.capturing = True self.redrawActions() def prepareRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.rubberBand.setWidth(1) self.rubberBand.setColor(color) self.rubberBand.show() def prepareTempRubberBand(self): color = QColor("red") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(1) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() def redrawRubberBand(self): self.canvas.scene().removeItem(self.rubberBand) self.prepareRubberBand() for i in range(len(self.capturedPoints)): point = self.capturedPoints[i] if point.__class__ == QgsPoint: vertexCoord = self.toMapCoordinatesV2(self.layer, self.capturedPoints[i]) vertexCoord = QgsPointXY(vertexCoord.x(), vertexCoord.y()) else: vertexCoord = self.toMapCoordinates(self.layer, self.capturedPoints[i]) self.rubberBand.addPoint(vertexCoord) def redrawTempRubberBand(self): if self.tempRubberBand != None: self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint( self.toMapCoordinates( self.layer, self.capturedPoints[len(self.capturedPoints) - 1])) def stopCapturing(self): self.deleteLabels() self.deleteVertices() if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.drawingLine = False self.movingVertices = False self.showingVertices = False self.capturing = False self.capturedPoints = [] self.canvas.refresh() self.redrawActions() def addEndingVertex(self, canvasPoint): mapPoint = self.toMapCoordinates(canvasPoint) layerPoint = self.toLayerCoordinates(self.layer, canvasPoint) self.rubberBand.addPoint(mapPoint) self.capturedPoints.append(layerPoint) self.tempRubberBand.reset(QgsWkbTypes.LineGeometry) self.tempRubberBand.addPoint(mapPoint) def removeLastVertex(self): if not self.capturing: return rubberBandSize = self.rubberBand.numberOfVertices() tempRubberBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if rubberBandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if rubberBandSize > 1: if tempRubberBandSize > 1: point = self.rubberBand.getPoint(0, rubberBandSize - 2) self.tempRubberBand.movePoint(tempRubberBandSize - 2, point) else: self.tempRubberBand.reset(self.bandType()) del self.capturedPoints[-1] def addVertex(self, pos): newCapturedPoints = [] for i in range(len(self.capturedPoints) - 1): newCapturedPoints.append(self.capturedPoints[i]) vertex1 = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex1 = self.toCanvasCoordinates( QgsPointXY(vertex1.x(), vertex1.y())) vertex2 = self.toMapCoordinates(self.layer, self.capturedPoints[i + 1]) currentVertex2 = self.toCanvasCoordinates( QgsPointXY(vertex2.x(), vertex2.y())) distance = self.distancePointLine(pos.x(), pos.y(), currentVertex1.x(), currentVertex1.y(), currentVertex2.x(), currentVertex2.y()) if distance <= maxDistanceHitTest: layerPoint = self.toLayerCoordinates(self.layer, pos) newCapturedPoints.append(layerPoint) newCapturedPoints.append(self.capturedPoints[len(self.capturedPoints) - 1]) self.capturedPoints = newCapturedPoints self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() def removeVertex(self, pos): deletedFirst = False deletedLast = False newCapturedPoints = [] for i in range(len(self.capturedPoints)): vertex = self.toMapCoordinates(self.layer, self.capturedPoints[i]) currentVertex = self.toCanvasCoordinates( QgsPointXY(vertex.x(), vertex.y())) if not self.distancePoint(pos, currentVertex) <= maxDistanceHitTest: newCapturedPoints.append(self.capturedPoints[i]) elif i == 0: deletedFirst = True elif i == len(self.capturedPoints) - 1: deletedLast = True self.capturedPoints = newCapturedPoints if deletedFirst and deletedLast: self.lineClosed = False self.redrawRubberBand() self.redrawVertices() self.redrawAreas() self.redrawActions() if len(self.capturedPoints) <= 2: self.stopRemovingVertices() def startMovingVertices(self): self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveVertices.setChecked(True) self.movingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingVertices(self): self.movingVertices = False self.actionMoveVertices.setChecked(False) self.restoreAction() def startAddingVertices(self): self.stopMovingVertices() self.stopRemovingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionAddVertices.setChecked(True) self.addingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopAddingVertices(self): self.addVertices = False self.actionAddVertices.setChecked(False) self.restoreAction() def startRemovingVertices(self): self.stopMovingVertices() self.stopAddingVertices() self.stopMovingLine() self.stopMovingSegment() self.actionRemoveVertices.setChecked(True) self.removingVertices = True self.showingVertices = True self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopRemovingVertices(self): self.removingVertices = False self.actionRemoveVertices.setChecked(False) self.restoreAction() def startMovingSegment(self): self.stopMovingVertices() self.stopMovingLine() self.stopAddingVertices() self.stopRemovingVertices() self.actionMoveSegment.setChecked(True) self.movingSegment = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawVertices() self.redrawAreas() self.redrawActions() def stopMovingSegment(self): self.movingSegment = False self.actionMoveSegment.setChecked(False) self.restoreAction() def startMovingLine(self): self.stopMovingVertices() self.stopAddingVertices() self.stopRemovingVertices() self.stopMovingSegment() self.actionMoveLine.setChecked(True) self.movingLine = True self.showingVertices = False self.drawingLine = False self.canvas.scene().removeItem(self.tempRubberBand) self.redrawAreas() self.redrawActions() def stopMovingLine(self): self.actionMoveLine.setChecked(False) self.restoreAction() def lineClose(self): self.lineClosed = True self.capturedPoints.append(self.capturedPoints[0]) self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def lineOpen(self): self.lineClosed = False del self.capturedPoints[-1] self.redrawRubberBand() self.redrawTempRubberBand() self.redrawAreas() self.redrawActions() def showVertices(self): for i in range(len(self.capturedPoints)): vertexc = self.toMapCoordinates(self.layer, self.capturedPoints[i]) vertexCoords = self.toCanvasCoordinates( QgsPointXY(vertexc.x(), vertexc.y())) if i == self.movingVertex: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) elif i == len(self.capturedPoints ) - 1 and self.movingVertex == 0 and self.lineClosed: vertex = self.scene.addRect(vertexCoords.x() - 5, vertexCoords.y() - 5, 10, 10, QPen(QColor("green")), QBrush(QColor("green"))) self.vertices.append(vertex) else: vertex = self.scene.addRect(vertexCoords.x() - 4, vertexCoords.y() - 4, 8, 8, QPen(QColor("red")), QBrush(QColor("red"))) self.vertices.append(vertex) def deleteVertices(self): for i in range(len(self.vertices)): self.scene.removeItem(self.vertices[i]) self.vertices = [] def lineMagnitude(self, x1, y1, x2, y2): return sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)) def distancePointLine(self, px, py, x1, y1, x2, y2): magnitude = self.lineMagnitude(x1, y1, x2, y2) if magnitude < 0.00000001: distance = 9999 return distance u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) u = u1 / (magnitude * magnitude) if (u < 0.00001) or (u > 1): ix = self.lineMagnitude(px, py, x1, y1) iy = self.lineMagnitude(px, py, x2, y2) if ix > iy: distance = iy else: distance = ix else: ix = x1 + u * (x2 - x1) iy = y1 + u * (y2 - y1) distance = self.lineMagnitude(px, py, ix, iy) return distance def redrawVertices(self): self.deleteVertices() self.showVertices() def redrawActions(self): self.redrawActionMoveVertices() self.redrawActionAddVertices() self.redrawActionRemoveVertices() self.redrawActionMoveSegment() self.redrawActionLineClose() self.redrawActionLineOpen() self.redrawActionMoveLine() def redrawActionMoveVertices(self): self.actionMoveVertices.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveVertices.setEnabled(True) def redrawActionAddVertices(self): self.actionAddVertices.setEnabled(False) if len(self.capturedPoints) >= 2: self.actionAddVertices.setEnabled(True) def redrawActionRemoveVertices(self): self.actionRemoveVertices.setEnabled(False) if len(self.capturedPoints) > 2: self.actionRemoveVertices.setEnabled(True) def redrawActionMoveSegment(self): self.actionMoveSegment.setEnabled(False) if len(self.capturedPoints) > 2: self.actionMoveSegment.setEnabled(True) def redrawActionLineClose(self): self.actionLineClose.setEnabled(False) if not self.lineClosed and len(self.capturedPoints) >= 3: self.actionLineClose.setEnabled(True) def redrawActionLineOpen(self): self.actionLineOpen.setEnabled(False) if self.lineClosed: self.actionLineOpen.setEnabled(True) def redrawActionMoveLine(self): self.actionMoveLine.setEnabled(False) if len(self.capturedPoints) > 0: self.actionMoveLine.setEnabled(True)
class VertexTracerTool(QgsMapTool): traceFound = pyqtSignal(QgsGeometry) def __init__(self, canvas): # some stuff we need from qgis QgsMapTool.__init__(self, canvas) self.canvas = canvas self.snapper = QgsSnappingUtils() # some stuff to control our state self.mCtrl = False self.started = False self.firstTimeOnSegment = True self.lastPointMustStay = False # some stuff to put our temp output self.lastPoint = None self.rb = None self.isPolygon = False # our own fancy cursor self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #00FF00", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) # we need to know, if ctrl-key is pressed def keyPressEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = True # and also if ctrl-key is released def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: self.mCtrl = False # remove the last added point when the delete key is pressed if event.key() == Qt.Key_Backspace: self.rb.removeLastPoint() def canvasPressEvent(self, event): # on left click, we add a point if event.button() == 1: layer = self.canvas.currentLayer() # if it is the start of a new trace, set the rubberband up if not self.started: if layer.geometryType() == 1: self.isPolygon = False if layer.geometryType() == 2: self.isPolygon = True self.rb = QgsRubberBand(self.canvas, layer.geometryType()) self.rb.setColor(QColor(255, 0, 0)) # declare, that are we going to work, now! self.started = True if layer is not None: x = event.pos().x() y = event.pos().y() selPoint = QPoint(x, y) # try to get something snapped (retval, result) = self.snapper.snapToBackgroundLayers(selPoint) # the point we want to have, is either from snapping result if result != []: point = result[0].snappedVertex # if we snapped something, it's either a vertex if result[0].snappedVertexNr != -1: self.firstTimeOnSegment = True # or a point on a segment, so we have to declare, that a point on segment is found else: self.firstTimeOnSegment = False # or its some point from out in the wild else: point = QgsMapToPixel.toMapCoordinates( self.canvas.getCoordinateTransform(), x, y) self.firstTimeOnSegment = True # bring the rubberband to the cursor i.e. the clicked point self.rb.movePoint(point) # and set a new point to go on with self.appendPoint(point) # try to remember that this point was on purpose i.e. clicked by the user self.lastPointMustStay = True self.firstTimeOnSegment = True def canvasMoveEvent(self, event): # QgsMessageLog.logMessage('fts: '+str(self.firstTimeOnSegment), 'state', 0) # QgsMessageLog.logMessage('lpc: '+str(self.lastPointMustStay), 'state', 0) if self.started: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # only if the ctrl key is pressed if self.mCtrl: layer = self.canvas.currentLayer() if layer is not None: (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # so if we have found a snapping if result != []: # pydevd.settrace() # that's the snapped point point = QgsPoint(result[0].snappedVertex) # if it is a vertex, not a point on a segment if result[0].snappedVertexNr != -1: self.rb.movePoint(point) self.appendPoint(point) self.lastPointMustStay = True # the next point found, may be on a segment self.firstTimeOnSegment = True # we are on a segment else: self.rb.movePoint(point) # if we are on a new segment, we add the point in any case if self.firstTimeOnSegment: self.appendPoint(point) self.lastPointMustStay = True self.firstTimeOnSegment = False # if we are not on a new segemnt, we have to test, if this point is realy needed else: # but only if we have already enough points # TODO: check if this is correct for lines also (they only need two points, to be vaild) if self.rb.numberOfVertices() >= 3: max = self.rb.numberOfVertices() lastRbP = self.rb.getPoint(0, max - 2) # QgsMessageLog.logMessage(str(self.rb.getPoint(0, max-1)), 'rb', 0) nextToLastRbP = self.rb.getPoint(0, max - 3) # QgsMessageLog.logMessage(str(self.rb.getPoint(0, max-2)), 'rb', 0) # QgsMessageLog.logMessage(str(point), 'rb', 0) if not self.pointOnLine( lastRbP, nextToLastRbP, QgsPoint(point)): self.appendPoint(point) self.lastPointMustStay = False else: # TODO: schauen, ob der letzte punkt ein klick war, dann nicht löschen. entsrpechend auch die "punkt auf linie" neu berechenen. if not self.lastPointMustStay: self.rb.removeLastPoint() self.rb.movePoint(point) # QgsMessageLog.logMessage('point removed', 'click', 0) # else: # QgsMessageLog.logMessage('sleep', 'rb', 0) else: self.appendPoint(point) self.lastPointMustStay = False self.firstTimeOnSegment = False else: # if nothing specials happens, just update the rubberband to the cursor position point = QgsMapToPixel.toMapCoordinates( self.canvas.getCoordinateTransform(), x, y) self.rb.movePoint(point) else: # in "not-tracing" state, just update the rubberband to the cursor position # but we have still to snap to act like the "normal" digitize tool (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) if result != []: point = QgsPoint(result[0].snappedVertex) else: point = QgsMapToPixel.toMapCoordinates( self.canvas.getCoordinateTransform(), x, y) self.rb.movePoint(point) def canvasReleaseEvent(self, event): # with right click the digitizing is finished if event.button() == 2: layer = self.canvas.currentLayer() x = event.pos().x() y = event.pos().y() if layer is not None and self.started: selPoint = QPoint(x, y) (retval, result) = self.snapper.snapToBackgroundLayers(selPoint) if result != []: point = result[0].snappedVertex else: point = QgsMapToPixel.toMapCoordinates( self.canvas.getCoordinateTransform(), x, y) # add this last point self.appendPoint(QgsPoint(point)) self.sendGeometry() def appendPoint(self, point): # don't add the point if it is identical to the last point we added if not (self.lastPoint == point): self.rb.addPoint(point) self.lastPoint = QgsPoint(point) else: pass # see: double QgsGeometryValidator::distLine2Point( QgsPoint p, QgsVector v, QgsPoint q ) # distance of point q from line through p in direction v def pointOnLine(self, pntAft, pntBef, pntTest): p = QgsPoint(pntAft) vx = pntBef.x() - pntAft.x() vy = pntBef.y() - pntAft.y() vlength = math.sqrt(vy * vy + vx * vx) if vlength == 0: return False q = QgsPoint(pntTest) res = (vx * (q.y() - p.y()) - vy * (q.x() - p.x())) / vlength # res = 0 means point is on line, but we are not in a perfect world, so a tolerance is needed # to get rid of some numerical problems. Tolerance estimated by solid engenieering work (rule of thumb...) if res < 1E-10: return True else: return False def sendGeometry(self): layer = self.canvas.currentLayer() coords = [] # backward compatiblity for a bug in qgsRubberband, that was fixed in 1.7 if Qgis.QGIS_VERSION_INT >= 10700: [ coords.append(self.rb.getPoint(0, i)) for i in range(self.rb.numberOfVertices()) ] else: [ coords.append(self.rb.getPoint(0, i)) for i in range(1, self.rb.numberOfVertices()) ] # On the Fly reprojection, not necessary any more, mapToLayerCoordinates is clever enough on its own # layerEPSG = layer.srs().epsg() # projectEPSG = self.canvas.mapRenderer().destinationSrs().epsg() # if layerEPSG != projectEPSG: coords_tmp = coords[:] coords = [] for point in coords_tmp: transformedPoint = self.canvas.mapRenderer().mapToLayerCoordinates( layer, point) coords.append(transformedPoint) coords_tmp = coords[:] coords = [] lastPt = None for pt in coords_tmp: if (lastPt != pt): coords.append(pt) lastPt = pt # Add geometry to feature. if self.isPolygon: g = QgsGeometry().fromPolygon([coords]) else: g = QgsGeometry().fromPolyline(coords) self.traceFound.emit(g) self.rb.reset(self.isPolygon) self.started = False def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): try: self.rb.reset() except AttributeError: pass def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True
class QgsMapToolCaptureSpatialOperand(QgsMapTool): """ QGIS map tool. Draw a point, line, polygon or bounding box and convert it to WKT format. """ selectionFinished = QtCore.pyqtSignal(str) def __init__(self, canvas, gmlOperand="gml:Envelope", srsName="", parent=None): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.parent = parent if gmlOperand == "gml:Point": self.minPoints = 1 self.maxPoints = 1 self.isPolygon = False elif gmlOperand == "gml:Envelope": self.minPoints = 2 self.maxPoints = 2 self.isPolygon = True elif gmlOperand == "gml:Polygon": self.minPoints = 3 self.maxPoints = 0 self.isPolygon = True elif gmlOperand == "gml:LineString": self.minPoints = 2 self.maxPoints = 0 self.isPolygon = False else: pass self.srsName = srsName self.rect = QtCore.QRect( ) if self.isPolygon and self.maxPoints == 2 else None if self.isPolygon: self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) else: self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) self.rubberBand.setColor(QtGui.QColor(255, 0, 0, 150)) self.rubberBand.setWidth(1) self.cursor = QtGui.QCursor(QtCore.Qt.CrossCursor) self.vertexMarkers = [] self.captureList = [] self.crs = QgsCoordinateReferenceSystem() self.crs.createFromUserInput(self.srsName) self.yx = self.crs.axisInverted() def canvasPressEvent(self, event): pass def canvasMoveEvent(self, event): if isinstance(self.rect, QtCore.QRect): self.rect.setBottomRight(event.pos()) self.moveVertex(self.toMapCoordinates(event.pos())) def canvasReleaseEvent(self, event): numPoints = self.addVertex(self.toMapCoordinates(event.pos())) if numPoints == 1 and isinstance(self.rect, QtCore.QRect): self.rect.setTopLeft(event.pos()) if (event.button() == QtCore.Qt.RightButton and numPoints >= self.minPoints) or \ (numPoints == self.maxPoints): self.finishGeom(numPoints) def keyReleaseEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.selectionFinished.emit(None) def activate(self): QgsMapTool.activate(self) self.canvas.setCursor(self.cursor) def deactivate(self): QgsMapTool.deactivate(self) self.canvas.unsetCursor() self.clearMapCanvas() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return False def addVertex(self, pt): self.rubberBand.addPoint(pt) m = QgsVertexMarker(self.canvas) m.setCenter(pt) self.vertexMarkers.append(m) #if self.yx: pt = QgsPoint(pt.y(),pt.x()) self.captureList.append(pt) return len(self.captureList) def moveVertex(self, pt): if self.rubberBand.numberOfVertices() > 1: if isinstance(self.rect, QtCore.QRect): transform = self.canvas.getCoordinateTransform() ll = transform.toMapCoordinates(self.rect.left(), self.rect.bottom()) ur = transform.toMapCoordinates(self.rect.right(), self.rect.top()) self.rubberBand.reset(QGis.Polygon) self.rubberBand.addPoint(ll, False) self.rubberBand.addPoint(QgsPoint(ur.x(), ll.y()), False) self.rubberBand.addPoint(ur, False) self.rubberBand.addPoint(QgsPoint(ll.x(), ur.y()), True) else: self.rubberBand.movePoint(pt) def finishGeom(self, numPoints): if self.maxPoints == 1: geom = QgsGeometry.fromPoint(self.captureList[0]) elif self.isPolygon and numPoints == 2: geom = QgsGeometry.fromPolyline(self.captureList) #geom = QgsGeometry.fromRect(geom.boundingBox()) elif self.isPolygon: geom = QgsGeometry.fromPolygon([self.captureList]) else: geom = QgsGeometry.fromPolyline(self.captureList) geom.simplify(0.00001) geom.transform( QgsCoordinateTransform(self.canvas.mapSettings().destinationCrs(), self.crs)) if self.yx: i = 0 vertex = geom.vertexAt(i) while (vertex != QgsPoint(0, 0)): x = vertex.x() y = vertex.y() geom.moveVertex(y, x, i) i += 1 vertex = geom.vertexAt(i) self.selectionFinished.emit(geom.exportToWkt()) self.clearMapCanvas() def clearMapCanvas(self): """ Clears the map canvas and in particular the rubberband. A warning is thrown when the markers are removed. """ # Reset the capture list self.captureList = [] # Create an empty rubber band if self.isPolygon: self.rubberBand.reset(QGis.Polygon) else: self.rubberBand.reset(QGis.Line) # Delete also all vertex markers for marker in self.vertexMarkers: self.canvas.scene().removeItem(marker) del marker self.canvas.refresh()
class MeasureTool(QgsMapTool): ''' classdocs ''' def __init__(self, canvas, txtBearing, disType=DistanceUnits.M): ''' Constructor ''' self.canvas = canvas self.txtBearing = txtBearing QgsMapTool.__init__(self, self.canvas) self.mSnapper = QgsMapCanvasSnapper(self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) self.rubberBand.setColor(Qt.red) self.rubberBand.setWidth(1) self.rubberBandPt = QgsRubberBand(canvas, QGis.Point) self.rubberBandPt.setColor(Qt.red) self.rubberBandPt.setWidth(10) self.type = disType self.reset() def reset(self): self.startPoint = None self.endPoint = None self.isDrawing = False self.distance = 0.0 def canvasReleaseEvent(self, e): if (e.button() == Qt.RightButton): self.reset() self.emit(SIGNAL("captureFinished")) else: self.rubberBandPt.reset(QGis.Point) snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas, True) if self.startPoint == None: self.rubberBand.reset(QGis.Line) if snapPoint == None: self.startPoint = self.toMapCoordinates(e.pos()) else: self.startPoint = snapPoint self.rubberBand.addPoint(self.startPoint) self.isDrawing = True else: if snapPoint == None: self.endPoint = self.toMapCoordinates(e.pos()) else: self.endPoint = snapPoint self.rubberBand.addPoint(self.endPoint) dist = MathHelper.calcDistance(self.startPoint, self.endPoint) self.distance = self.distance + dist if self.type == DistanceUnits.M: self.txtBearing.setText("%f" % round(self.distance, 4)) elif self.type == DistanceUnits.NM: self.txtBearing.setText( "%f" % round(Unit.ConvertMeterToNM(self.distance), 4)) elif self.type == DistanceUnits.FT: self.txtBearing.setText( "%f" % round(Unit.ConvertMeterToFeet(self.distance), 4)) elif self.type == DistanceUnits.KM: self.txtBearing.setText("%f" % round( (self.distance / 1000), 4)) elif self.type == DistanceUnits.MM: self.txtBearing.setText(str(int(self.distance * 1000))) self.startPoint = self.endPoint def canvasMoveEvent(self, e): self.rubberBandPt.reset(QGis.Point) snapPoint = QgisHelper.snapPoint(e.pos(), self.mSnapper, define._canvas, True) if snapPoint != None: self.rubberBandPt.addPoint(snapPoint) self.rubberBandPt.show() if self.isDrawing: if self.isDrawing: if snapPoint == None: self.endPoint = self.toMapCoordinates(e.pos()) else: self.endPoint = snapPoint self.rubberBand.movePoint(self.endPoint) dist1 = MathHelper.calcDistance(self.startPoint, self.endPoint) dist1 = self.distance + dist1 if self.type == DistanceUnits.M: self.txtBearing.setText("%f" % round(dist1, 4)) elif self.type == DistanceUnits.NM: self.txtBearing.setText("%f" % round(Unit.ConvertMeterToNM(dist1), 4)) elif self.type == DistanceUnits.FT: self.txtBearing.setText( "%f" % round(Unit.ConvertMeterToFeet(dist1), 4)) elif self.type == DistanceUnits.KM: self.txtBearing.setText("%f" % round((dist1 / 1000), 4)) elif self.type == DistanceUnits.MM: self.txtBearing.setText(str(int(dist1 * 1000))) def deactivate(self): self.rubberBandPt.reset(QGis.Point) QgsMapTool.deactivate(self) self.emit(SIGNAL("deactivated()"))