def getIntersection(self, p1, p2): """ Get intersection on segment :param p1: QgsPointV2 :param p2: QgsPointV2 :param inter: QgsPointV2 reference :return: bool """ q1 = self.idxFrom.point() q2 = self.idxTo.point() v = QgsVector(p2.x() - p1.x(), p2.y() - p1.y()) w = QgsVector(q2.x() - q1.x(), q2.y() - q1.y()) vl = v.length() wl = w.length() if self.isclose(vl, 0.) or self.isclose(wl, 0.): return None v = v / vl w = w / wl d = v.y() * w.x() - v.x() * w.y() if d == 0: return None dx = q1.x() - p1.x() dy = q1.y() - p1.y() k = (dy * w.x() - dx * w.y()) / d inter = QgsPointV2(p1.x() + v.x() * k, p1.y() + v.y() * k) lambdav = QgsVector(inter.x() - p1.x(), inter.y() - p1.y()) * v if lambdav < 0. + 1E-8 or lambdav > vl - 1E-8: return None lambdaw = QgsVector(inter.x() - q1.x(), inter.y() - q1.y()) * w if lambdaw < 0. + 1E-8 or lambdaw >= wl - 1E-8: return None return inter
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 MoveTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.dock_widget = data_dock """:type : DataDock""" self.params = params self.elev = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.mouse_clicked = False self.snapper = None self.clicked_pt = None self.snap_results = None self.selected_node_ft = None self.selected_node_ft_lay = None self.mouse_pt = None self.pump_valve_selected = False self.pump_or_valve = None self.pump_valve_ft = None self.adj_links_fts = None self.adj_junctions = None self.delta_vec = QgsVector(0, 0) self.adj_links_fts_d = {} # self.rubber_bands_d = {} # self.rubber_bands_geoms_d = {} self.rubber_band = None self.logger = logging.getLogger('Logger1') def canvasPressEvent(self, event): if self.snap_results is None: self.clicked_pt = None return if event.button() == Qt.RightButton: self.mouse_clicked = False self.clicked_pt = None if event.button() == Qt.LeftButton: self.mouse_clicked = True self.clicked_pt = self.snap_results.point() # Check if a node was snapped: it can be just one self.selected_node_ft = None self.adj_links_fts = None # Check if I snapped on a node snapped_layer_name = self.snap_results.layer().name() self.selected_node_ft = None pt_locator_ju = self.snapper.locatorForLayer(self.params.junctions_vlay) pt_locator_re = self.snapper.locatorForLayer(self.params.reservoirs_vlay) pt_locator_ta = self.snapper.locatorForLayer(self.params.tanks_vlay) match_ju = pt_locator_ju.nearestVertex(self.snap_results.point(), 1) match_re = pt_locator_re.nearestVertex(self.snap_results.point(), 1) match_ta = pt_locator_ta.nearestVertex(self.snap_results.point(), 1) if match_ju.isValid() or match_re.isValid() or match_ta.isValid(): if match_ju.isValid(): node_feat_id = match_ju.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.junctions_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.junctions_vlay if match_re.isValid(): node_feat_id = match_re.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.reservoirs_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.reservoirs_vlay if match_ta.isValid(): node_feat_id = match_ta.featureId() request = QgsFeatureRequest().setFilterFid(node_feat_id) node = list(self.params.tanks_vlay.getFeatures(request)) self.selected_node_ft_lay = self.params.tanks_vlay self.selected_node_ft = QgsFeature(node[0]) # self.selected_node_ft_lay = self.snap_results.layer() self.adj_links_fts = NetworkUtils.find_adjacent_links(self.params, self.selected_node_ft.geometry()) # No selected nodes: it's just a vertex if self.selected_node_ft is None: snapped_ft = vector_utils.get_feats_by_id(self.snap_results.layer(), self.snap_results.featureId())[0] snapped_ft_geom = snapped_ft.geometry() points = [] if self.snap_results.vertexIndex() - 1 >= 0 and self.snap_results.vertexIndex() - 1 < len(snapped_ft_geom.asPolyline()): vertex_index = 1 vertex_before = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() - 1) points.append(vertex_before) vertex_at = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex()) points.append(vertex_at) if self.snap_results.vertexIndex() + 1 >= 0 and self.snap_results.vertexIndex() + 1 < len(snapped_ft_geom.asPolyline()): vertex_index = 0 vertex_after = snapped_ft_geom.vertexAt(self.snap_results.vertexIndex() + 1) points.append(vertex_after) if self.snap_results.vertexIndex() > 0 and self.snap_results.vertexIndex() < len(snapped_ft_geom.asPolyline()) - 1: vertex_index = 1 # self.rubber_bands_d[0] = (self.build_rubber_band(points), [vertex_index], [vertex_at]) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) # It's a node else: # It's an isolated node: no rubber band! if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']): # self.rubber_bands_d.clear() self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) return # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node # OR node adjacent to pump or valve and NOT using block logic if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic: self.pump_valve_selected = False rb_points = [] for adjacent_pipes_ft in self.adj_links_fts['pipes']: closest = adjacent_pipes_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adjacent_pipes_ft] = (closest[1], self.params.pipes_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adjacent_pipes_ft.geometry().vertexAt(next_vertext_id)) for adj_pumps_ft in self.adj_links_fts['pumps']: closest = adj_pumps_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adj_pumps_ft] = (closest[1], self.params.pumps_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adj_pumps_ft.geometry().vertexAt(next_vertext_id)) for adj_valves_ft in self.adj_links_fts['valves']: closest = adj_valves_ft.geometry().closestVertex(self.selected_node_ft.geometry().asPoint()) self.adj_links_fts_d[adj_valves_ft] = (closest[1], self.params.valves_vlay) if closest[1] == 0: next_vertext_id = closest[1] + 1 else: next_vertext_id = closest[1] - 1 rb_points.append(adj_valves_ft.geometry().vertexAt(next_vertext_id)) rb_points.insert(1, self.selected_node_ft.geometry().asPoint()) # self.rubber_bands_d[0] = (self.build_rubber_band(rb_points), [1], [self.selected_node_ft.geometry().asPoint()]) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) # Node adjacent to pump or valve and using block logic else: self.pump_valve_selected = True # Find the pipes adjacent to the pump/valve if self.adj_links_fts['pumps']: self.pump_valve_ft = self.adj_links_fts['pumps'][0] self.pump_or_valve = 'pump' adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.pumps_vlay, self.pump_valve_ft, False, True, True) elif self.adj_links_fts['valves']: self.pump_valve_ft = self.adj_links_fts['valves'][0] self.pump_or_valve = 'valve' adj_links = NetworkUtils.find_links_adjacent_to_link(self.params, self.params.valves_vlay, self.pump_valve_ft, False, True, True) else: return pump_valve_pts = self.pump_valve_ft.geometry().asPolyline() # self.rubber_bands_d[0] = (self.build_rubber_band(pump_valve_pts), range(len(pump_valve_pts)), pump_valve_pts) self.rubber_band = self.build_rubber_band([self.snap_results.point(), self.snap_results.point()]) rb_index = 1 if 'pipes' in adj_links: for adj_link_ft in adj_links['pipes']: self.process_adj(adj_link_ft, self.params.pipes_vlay, rb_index) rb_index += 1 if 'pumps' in adj_links: for adj_link_ft in adj_links['pumps']: self.process_adj(adj_link_ft, self.params.pumps_vlay, rb_index) rb_index += 1 if 'valves' in adj_links: for adj_link_ft in adj_links['valves']: self.process_adj(adj_link_ft, self.params.valves_vlay, rb_index) rb_index += 1 # Find the nodes adjacent to the pump/valve self.adj_junctions = NetworkUtils.find_start_end_nodes_w_layer(self.params, self.pump_valve_ft.geometry()) def process_adj(self, adj_link_ft, layer, rb_index): adj_link_pts = adj_link_ft.geometry().asPolyline() pump_valve_pts = self.pump_valve_ft.geometry().asPolyline() if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[0], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (0, layer) # rb_pts = [pump_valve_pts[0], adj_link_pts[1]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[0], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (0, layer) # rb_pts = [pump_valve_pts[-1], adj_link_pts[1]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[0]), adj_link_pts[-1], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer) # rb_pts = [pump_valve_pts[0], adj_link_pts[-2]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[0]]) if NetworkUtils.points_overlap(QgsGeometry.fromPoint(pump_valve_pts[-1]), adj_link_pts[-1], self.params.tolerance): self.adj_links_fts_d[adj_link_ft] = (len(adj_link_pts)-1, layer) # rb_pts = [pump_valve_pts[-1], adj_link_pts[-2]] # self.rubber_bands_d[rb_index] = (self.build_rubber_band(rb_pts), [0], [pump_valve_pts[-1]]) def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) if elev is not None: self.elev = elev self.dock_widget.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.elev = None self.dock_widget.lbl_elev_val.setText('-') # Mouse not clicked if not self.mouse_clicked: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): self.snap_results = match # snapped_pt = self.snap_results[0].snappedVertex snapped_vertex = match.point() self.vertex_marker.setCenter(QgsPoint(snapped_vertex.x(), snapped_vertex.y())) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: self.snap_results = None self.selected_node_ft = None self.vertex_marker.hide() # Mouse clicked else: # Update rubber band if self.snap_results is not None and self.rubber_band: snapped_pt = self.snap_results.point() # In 2.16+: self.delta_vec = QgsVector(self.mouse_pt - snapped_pt) self.delta_vec = QgsVector(self.mouse_pt.x() - snapped_pt.x(), self.mouse_pt.y() - snapped_pt.y()) self.move_rubber_band_pt(self.rubber_band) # if self.adj_links_fts is None or (not self.adj_links_fts['pipes'] and not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']): # # There are no adjacent links # self.move_rubber_band_pt(self.rubber_bands_d[0]) # else: # # # It's just a junction # if not self.pump_valve_selected: # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) # # # It's a pump/valve # else: # # # Adjacent links are neither pumps nor valves: find the two pipes adjacent to the node # # Or node adjacent to pump or valve and NOT using block logic # if (not self.adj_links_fts['pumps'] and not self.adj_links_fts['valves']) or not self.params.block_logic: # # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) # # in 2.16: self.rubber_bands_d[key].movePoint(1, snapped_pt + self.delta_vec) # # self.rubber_bands_d[key].movePoint(2, QgsPoint(snapped_pt.x() + self.delta_vec.x(), snapped_pt.y() + self.delta_vec.y())) # # # Node adjacent to pump or valve and using block logic # else: # for key, value in self.rubber_bands_d.iteritems(): # self.move_rubber_band_pt(self.rubber_bands_d[key]) def move_rubber_band_pt(self, rubber_band_v): # rubber_band = rubber_band_v[0] # pt_indices = rubber_band_v[1] # start_pts = rubber_band_v[2] # for i in range(len(pt_indices)): # rubber_band.movePoint(pt_indices[i], QgsPoint(start_pts[i].x() + self.delta_vec.x(), start_pts[i].y() + self.delta_vec.y())) rubber_band_v.movePoint(1, QgsPoint(self.clicked_pt.x() + self.delta_vec.x(), self.clicked_pt.y() + self.delta_vec.y())) def canvasReleaseEvent(self, event): mouse_pt = self.toMapCoordinates(event.pos()) if not self.mouse_clicked: return if event.button() == 1: self.mouse_clicked = False if self.snap_results is not None: snap_results = self.snap_results selected_node_ft = self.selected_node_ft selected_node_ft_lay = self.selected_node_ft_lay # Check elev if self.elev is None and self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', QgsMessageBar.WARNING, 5) # TODO: softcode # It's just a pipe vertex if selected_node_ft is None: feat = vector_utils.get_feats_by_id(snap_results.layer(), snap_results.featureId()) vertex_id = QgsVertexId(0, 0, snap_results.vertexIndex(), QgsVertexId.SegmentVertex) vertex_v2 = feat[0].geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, self.params.pipes_vlay, feat[0], new_pos_pt_v2, snap_results.vertexIndex()) # There are adjacent links: it's a node else: # Not pump or valve: plain junction if not self.pump_valve_selected: # Update junction geometry NodeHandler.move_element( selected_node_ft_lay, self.params.dem_rlay, selected_node_ft, mouse_pt) # Update pipes for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems(): vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex) vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2(mouse_pt.x(), mouse_pt.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index) # Pump or valve else: # Update junctions geometry NodeHandler.move_element(self.adj_junctions[0][1], self.params.dem_rlay, self.adj_junctions[0][0], QgsPoint( self.adj_junctions[0][0].geometry().asPoint().x() + self.delta_vec.x(), self.adj_junctions[0][0].geometry().asPoint().y() + self.delta_vec.y())) NodeHandler.move_element(self.adj_junctions[1][1], self.params.dem_rlay, self.adj_junctions[1][0], QgsPoint( self.adj_junctions[1][0].geometry().asPoint().x() + self.delta_vec.x(), self.adj_junctions[1][0].geometry().asPoint().y() + self.delta_vec.y())) # in 2.16: NodeHandler.move_element(Parameters.junctions_vlay, self.adj_links_fts[0], self.adj_links_fts[0].geometry().asPoint() + self.delta_vec) if self.pump_or_valve == 'pump': lay = self.params.pumps_vlay elif self.pump_or_valve == 'valve': lay = self.params.valves_vlay # Move the pump/valve LinkHandler.move_pump_valve(lay, self.pump_valve_ft, self.delta_vec) # Move the adjacent pipes' vertices for feat, (vertex_index, layer) in self.adj_links_fts_d.iteritems(): vertex_id = QgsVertexId(0, 0, vertex_index, QgsVertexId.SegmentVertex) vertex_v2 = feat.geometry().geometry().vertexAt(vertex_id) new_pos_pt_v2 = QgsPointV2( feat.geometry().vertexAt(vertex_index).x() + self.delta_vec.x(), feat.geometry().vertexAt(vertex_index).y() + self.delta_vec.y()) new_pos_pt_v2.addZValue(vertex_v2.z()) LinkHandler.move_link_vertex(self.params, layer, feat, new_pos_pt_v2, vertex_index) # In 2.16: LinkHandler.move_pipe_vertex(feat, feat.geometry().vertexAt(vertex_index) + self.delta_vec, vertex_index) self.adj_links_fts_d.clear() symbology.refresh_layer(self.iface.mapCanvas(), self.params.junctions_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.reservoirs_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.tanks_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pipes_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pumps_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.valves_vlay) # Remove vertex marker and rubber band self.vertex_marker.hide() self.iface.mapCanvas().scene().removeItem(self.rubber_band) def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) # Snapping layers = { self.params.junctions_vlay: QgsPointLocator.Vertex, self.params.reservoirs_vlay: QgsPointLocator.Vertex, self.params.tanks_vlay: QgsPointLocator.Vertex, self.params.pipes_vlay: QgsPointLocator.Vertex} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.reservoirs_vlay.isEditable(): self.params.reservoirs_vlay.startEditing() if not self.params.tanks_vlay.isEditable(): self.params.tanks_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.pumps_vlay.isEditable(): self.params.pumps_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): # self.rubber_bands.clear() self.canvas().scene().removeItem(self.vertex_marker) self.dock_widget.btn_move_element.setChecked(False) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def build_rubber_band(self, points): rubber_band = QgsRubberBand(self.canvas(), False) # False = not a polygon rubber_band.setToGeometry(QgsGeometry.fromPolyline(points), None) # for point in points: # rubber_band.addPoint(point) rubber_band.setColor(QColor(255, 128, 128)) rubber_band.setWidth(1) rubber_band.setBrushStyle(Qt.Dense4Pattern) return rubber_band
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()