def start_to_rotate(self, event): mapPt,layerPt = self.transformCoordinates(event.pos()) color = QColor("black") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(4) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.tempRubberBand.addPoint(mapPt) self.startRotate = True
def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberband = QgsRubberBand(self.canvas, QGis.Polygon) self.point = None self.points = [] attribs = ['id'] types = [QtCore.QVariant.String] self.layer = uf.createTempLayer('Danger Zones', 'POLYGON', '28992', attribs, types, 50) self.symbols = self.layer.rendererV2().symbols() self.symbol = self.symbols[0] self.symbol.setColor(QColor.fromRgb(160, 160, 160))
class PolyMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.rubberband = QgsRubberBand(self.canvas, QGis.Polygon) self.point = None self.points = [] attribs = ['id'] types = [QtCore.QVariant.String] self.layer = uf.createTempLayer('Danger Zones', 'POLYGON', '28992', attribs, types, 50) self.symbols = self.layer.rendererV2().symbols() self.symbol = self.symbols[0] self.symbol.setColor(QColor.fromRgb(160, 160, 160)) def canvasPressEvent(self, e): self.point = self.toMapCoordinates(e.pos()) self.points.append(self.point) self.showPoly(e) def showPoly(self, e): self.rubberband.reset(QGis.Polygon) for point in self.points[:-1]: self.rubberband.addPoint(point, False) self.rubberband.addPoint(self.points[-1], True) if e.button() == Qt.RightButton: self.poly() self.point = None self.points = [] self.rubberband.reset(QGis.Polygon) def poly(self): geom = self.rubberband.asGeometry() # Set the provider to accept the data source pr = self.layer.dataProvider() # Add a new feature and assign the geometry feat = QgsFeature() feat.setGeometry(geom) pr.addFeatures([feat]) # Update extent of the layer self.layer.updateExtents() self.refreshCanvas(self.layer) # Add the layer to the Layers panel QgsMapLayerRegistry.instance().addMapLayers([self.layer]) def refreshCanvas(self, layer): if self.canvas.isCachingEnabled(): layer.setCacheImage(None) else: self.canvas.refresh()
def startCapturing(self): #rubberband voor de al vastgelegde punten color = QColor("red") color.setAlphaF(0.2) self.rubberBand = QgsRubberBand(self.canvas, self.bandType()) self.rubberBand.setWidth(2) self.rubberBand.setColor(color) self.rubberBand.show() #gestippelde rubberband -> voor het tekenen self.tempRubberBand = QgsRubberBand(self.canvas, self.bandType()) self.tempRubberBand.setWidth(2) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() #2x loodrechte hulp tekenlijnen self.perpRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.perpRubberBand.setWidth(1) self.perpRubberBand.setColor(QColor("blue")) self.perpRubberBand.setLineStyle(Qt.DotLine) self.perpRubberBand.show() self.perpRubberBand2 = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.perpRubberBand2.setWidth(1) self.perpRubberBand2.setColor(QColor("blue")) self.perpRubberBand2.setLineStyle(Qt.DotLine) self.perpRubberBand2.show() self.roundRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.roundRubberBand.setWidth(1) borderColor = QColor("blue") borderColor.setAlphaF(0.5) fillColor = QColor("blue") fillColor.setAlphaF(0) self.roundRubberBand.setFillColor(fillColor) self.roundRubberBand.setStrokeColor(borderColor) self.roundRubberBand.setLineStyle(Qt.DotLine) self.roundRubberBand.show() self.parent.straal.valueChanged.connect(self.roundrubberband_change_straal) self.capturing = True
class CaptureTool(QgsMapTool): CAPTURE_LINE = 1 CAPTURE_POLYGON = 2 def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.layer = None self.captureMode = None self.onGeometryAdded = None self.rubberBand = None self.tempRubberBand = None self.perpRubberBand = None self.perpRubberBand2 = None self.capturedPoints = [] self.capturing = False self.snapPt = None self.snapFeature = [] self.vertexmarker = None self.snapLayer = [] self.parent = None self.shiftPressed = False self.setCursor(Qt.CrossCursor) #actie gekoppeld aan the mouse release event def canvasReleaseEvent(self, event): #als the perpendicular rubberbands bestaan reset zodat ze opnieuw kunnen worden getekend if self.perpRubberBand != None: self.perpRubberBand.reset() if self.perpRubberBand2 != None: self.perpRubberBand2.reset() #als er met de linker muis geklikt wordt en er wordt nog niet getekend -> start het tekenen #anders voeg het aangeklikte punt toe aan de verzameling if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() if not self.shiftPressed: self.addVertex(event.pos()) else: self.drawParallel(event.pos()) #indien het de rechter muisknop is -> stop het tekenen en vertaal de punten tot een geometrie elif event.button() == Qt.RightButton: points = self.getCapturedGeometry() self.stopCapturing() if points != None: self.geometryCaptured(points) #acties gekoppeld aan het bewegen van de muis def canvasMoveEvent(self, event): #converteer de muislocatie naar laag en scherm coordinaten mapPt,layerPt = self.transformCoordinates(event.pos()) #kijk of er mogelijk gesnapt kan worden self.snapPt = self.snap_to_point(event.pos(), layerPt) if self.capturing: self.tempRubberBand.movePoint(mapPt) #pas de "gestippelde" rubberband aan aan de muispositie if self.captureMode == CaptureTool.CAPTURE_LINE and self.tempRubberBand != None and self.capturing: try: distance = QgsDistanceArea() m = distance.measureLine(self.tempRubberBand.getPoint(0, 0), mapPt) self.parent.lengte.setText(str(round(m,2)) + ' meter') self.parent.oppervlakte.setText('n.v.t.') except: None if self.captureMode == CaptureTool.CAPTURE_POLYGON and len(self.capturedPoints) >= 1 and self.capturing: distance = QgsDistanceArea() tempBandSize = self.tempRubberBand.numberOfVertices() m = distance.measureLine(self.tempRubberBand.getPoint(0, tempBandSize - 2), mapPt) self.parent.lengte.setText(str(round(m,2)) + ' meter') try: polygon = self.rubberBand.asGeometry().asPolygon()[0] temppolygon = self.tempRubberBand.asGeometry().asPolygon()[0] area = QgsDistanceArea() a = area.measurePolygon(polygon) b = area.measurePolygon(temppolygon) self.parent.oppervlakte.setText(str(round(a + b,2)) + ' vierkante-meter') except: None #laat standaard het snappunt niet zien tenzij er gesnapt kan worden self.vertexmarker.hide() if self.snapPt != None: self.vertexmarker.setCenter(self.snapPt) self.vertexmarker.show() #check of er gesnapt kan worden def snap_to_point(self, pos, layerPt): snapPoints = [] distance = [] self.snapFeature.clear() snapPoint = None val = None idx = None #initialiseer the vertexmarker (virtueel snappunt) if self.vertexmarker == None: self.vertexmarker = QgsVertexMarker(self.canvas) self.vertexmarker.setColor(QColor(255, 0, 255)) self.vertexmarker.setIconSize(8) self.vertexmarker.setIconType(QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.vertexmarker.setPenWidth(5) self.vertexmarker.show() #creeer lijst met alle mogelijke snappunten self.snapFeature = [] #max snap tolerantie tolerance = pow(self.calcTolerance(pos),2) #bereken snap "vierkant" rondom muis lokatie extent = self.calcExtent(layerPt, tolerance) #vraag mogelijke features op binnen het extent request = QgsFeatureRequest() request.setFilterRect(extent) request.setFlags(QgsFeatureRequest.ExactIntersect) #bereken voor alle mogelijke snap lagen: for ilayer in self.snapLayer: index = None igeometry = None ifeature = None polygon = [] tuple = None distSquared = None vertexCoord = None vertex = None prevVertex = None nextVertex = None #bereken een spatial index if type(ilayer) != QgsRubberBand: try: index = QgsSpatialIndex(ilayer.getFeatures(request)) if index != None: #bereken de dichtsbijzijnde feature per snaplaag nearestId = index.nearestNeighbor(layerPt, 1)[0] ifeature = next(ilayer.getFeatures(QgsFeatureRequest(nearestId))) igeometry = ifeature.geometry() #return dichtsbijzijnde lijnsegment van de features geometrie tuple = igeometry.closestSegmentWithContext(layerPt) #return dichtsbijzijnde punt van de features geometrie vertexCoord,vertex,prevVertex,nextVertex,distSquared = igeometry.closestVertex(layerPt) except: None #snappen op bestaande "rubberband" lagen moet ook mogelijk zijn elif type(ilayer) == QgsRubberBand: #converyt rubberband geometry to QgsGeometry igeometry = ilayer.asGeometry() if igeometry != None: tuple = igeometry.closestSegmentWithContext(layerPt) vertexCoord,vertex,prevVertex,nextVertex,distSquared = igeometry.closestVertex(layerPt) #als er een mogelijk snappunt is gevonden en deze valt binnen de tolerantie: #kijk eerst naar hoekpunt en dan pas naar lijnsegment(variabele -> tuple) (de voorkeur heeft snappen op hoekpunten) if distSquared != None and distSquared <= tolerance and igeometry != None and igeometry.isNull() == False: if type(ilayer) == QgsRubberBand: polygon = igeometry.asPolyline() elif ilayer.wkbType() == QgsWkbTypes.MultiLineString: polygon = igeometry.asMultiPolyline()[0] elif ilayer.wkbType() == 1003 or ilayer.wkbType() == 6: polygon = igeometry.asMultiPolygon()[0][0] else: polygon = igeometry.asPolygon()[0] #voeg dit punt toe aan de mogelijke snappunten if polygon != [] and vertexCoord != QgsPointXY(0,0): distance.append(distSquared) snapPoints.append([vertexCoord, vertex, prevVertex, polygon]) elif tuple != None and tuple[1] != QgsPointXY(0,0): if tuple[0] <= tolerance: distance.append(tuple[0]) snapPoints.append([tuple[1], None, None, None]) else: #Als het snappunt niet binnen de tolerantie valt, voeg dan een grote integer toe als snap afstand distance.append(tolerance + 9999) snapPoints.append([None, None, None, None]) #als er mogelijke snappunten zijn: if len(distance) > 0: #bereken de korste afstand met zijn id -> dit wordt het snappunt val, idx = min((val, idx) for (idx, val) in enumerate(distance)) #construct the point to return as snappoint if val != None: if val <= tolerance: if type(ilayer) == QgsRubberBand: snapPoint = snapPoints[idx][0] else: snapPoint = self.toMapCoordinates(ilayer, snapPoints[idx][0]) #sla zowel de feature als het punt op...de feature is nodig voor de perpendicular rubberbands if snapPoints[idx][1] != None: self.snapFeature.extend((snapPoints[idx][1], snapPoints[idx][2], snapPoints[idx][3])) else: self.snapFeature.extend((None, None, None)) if snapPoint != None: return snapPoint else: return None #bereken snap extent def calcExtent(self, layerPt, tolerance): extent = QgsRectangle(layerPt.x() - tolerance, layerPt.y() - tolerance, layerPt.x() + tolerance, layerPt.y() + tolerance) return extent #bereken de tolerantie def calcTolerance(self, pos): pt1 = QPoint(pos.x(), pos.y()) pt2 = QPoint(pos.x() + 10, pos.y()) mapPt1,layerPt1 = self.transformCoordinates(pt1) mapPt2,layerPt2 = self.transformCoordinates(pt2) tolerance = layerPt2.x() - layerPt1.x() return tolerance #indien de gebruiker op backspace of delete klikt verwijder het laatst geregistreerde punt #indien de gebruiker op enter drukt betekent hetzelfde als de rechtermuisknop 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_Return or event.key() == Qt.Key_Enter: points = self.getCapturedGeometry() self.stopCapturing() if points != None: self.geometryCaptured(points) if event.key() == Qt.Key_Shift: self.shiftPressed = True def keyReleaseEvent(self, event): if event.key() == Qt.Key_Shift: self.shiftPressed = False #transform een point to map coordinates and layer coordinates def transformCoordinates(self, canvasPt): return (self.toMapCoordinates(canvasPt), self.toLayerCoordinates(self.layer, canvasPt)) #bij starten van het tekenen intialiseer de rubberbands def startCapturing(self): #rubberband voor de al vastgelegde punten color = QColor("red") color.setAlphaF(0.2) self.rubberBand = QgsRubberBand(self.canvas, self.bandType()) self.rubberBand.setWidth(2) self.rubberBand.setColor(color) self.rubberBand.show() #gestippelde rubberband -> voor het tekenen self.tempRubberBand = QgsRubberBand(self.canvas, self.bandType()) self.tempRubberBand.setWidth(2) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() #2x loodrechte hulp tekenlijnen self.perpRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.perpRubberBand.setWidth(1) self.perpRubberBand.setColor(QColor("blue")) self.perpRubberBand.setLineStyle(Qt.DotLine) self.perpRubberBand.show() self.perpRubberBand2 = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.perpRubberBand2.setWidth(1) self.perpRubberBand2.setColor(QColor("blue")) self.perpRubberBand2.setLineStyle(Qt.DotLine) self.perpRubberBand2.show() self.roundRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.roundRubberBand.setWidth(1) borderColor = QColor("blue") borderColor.setAlphaF(0.5) fillColor = QColor("blue") fillColor.setAlphaF(0) self.roundRubberBand.setFillColor(fillColor) self.roundRubberBand.setStrokeColor(borderColor) self.roundRubberBand.setLineStyle(Qt.DotLine) self.roundRubberBand.show() self.parent.straal.valueChanged.connect(self.roundrubberband_change_straal) self.capturing = True #bepaal het type rubberband (polygoon of line) def bandType(self): if self.captureMode == CaptureTool.CAPTURE_POLYGON: return QgsWkbTypes.PolygonGeometry else: return QgsWkbTypes.LineGeometry #reset rubberbands als er gestopt wordt met tekenen def stopCapturing(self): if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None if self.roundRubberBand: self.canvas.scene().removeItem(self.roundRubberBand) self.roundRubberBand = None if self.perpRubberBand: self.canvas.scene().removeItem(self.perpRubberBand) self.perpRubberBand = None if self.perpRubberBand2: self.canvas.scene().removeItem(self.perpRubberBand2) self.perpRubberBand2 = None self.vertexmarker.hide() self.capturing = False self.capturedPoints = [] self.canvas.refresh() #bepaal het daadwerkelijk toe te voegen punt (snappunt of geklikt punt) def addVertex(self, canvasPoint): polygon = None clickedPt = None if self.snapPt != None: if self.snapFeature[2] != None and self.snapFeature[1] != None: polygon = self.snapFeature[2] clickedPt = polygon[self.snapFeature[1]] else: clickedPt = self.toLayerCoordinates(self.layer, canvasPoint) layerPt = self.toLayerCoordinates(self.layer, self.snapPt) mapPt = self.snapPt #bereken de snaphoek van het geklikte punt ten opzichte van het snappunt snapAngle = clickedPt.azimuth(layerPt) self.draw_perpendicularBand(layerPt, snapAngle) else: mapPt,layerPt = self.transformCoordinates(canvasPoint) if len(self.capturedPoints) > 0: perpPt = self.capturedPoints[-1] snapAngle = layerPt.azimuth(perpPt) + 90 self.draw_perpendicularBand(layerPt, snapAngle) #voeg het nieuwe map punt toe aan de rubberband self.rubberBand.addPoint(mapPt) #voeg het nieuwe layer punt toe aan de verzamelde punten self.capturedPoints.append(layerPt) #reset de temprubberband t.b.v. het volgende punt self.tempRubberBand.reset(self.bandType()) if self.captureMode == CaptureTool.CAPTURE_LINE: self.tempRubberBand.addPoint(mapPt) elif self.captureMode == CaptureTool.CAPTURE_POLYGON: firstPoint = self.rubberBand.getPoint(0, 0) self.tempRubberBand.addPoint(firstPoint) self.tempRubberBand.movePoint(mapPt) self.tempRubberBand.addPoint(mapPt) #bepaal het daadwerkelijk toe te voegen punt (snappunt of geklikt punt) def drawParallel(self, canvasPoint): clickedPt = None if self.snapPt != None and self.snapFeature[2] == None and len(self.capturedPoints) > 0: clickedPt = self.toLayerCoordinates(self.layer, canvasPoint) layerPt = self.toLayerCoordinates(self.layer, self.snapPt) mapPt = self.snapPt #bereken de snaphoek van het geklikte punt ten opzichte van het snappunt snapAngle = clickedPt.azimuth(layerPt) + 90 bandSize = self.rubberBand.numberOfVertices() lastPt = self.rubberBand.getPoint(0, bandSize-1) self.draw_perpendicularBand(lastPt, snapAngle) def roundrubberband_change_straal(self): straal = self.parent.straal.value() startPt = self.capturedPoints[-1] test = QgsCircle(QgsPoint(startPt), straal) geom_cString = test.toCircularString() geom_from_curve = QgsGeometry(geom_cString) self.roundRubberBand.setToGeometry(geom_from_curve) def draw_perpendicularBand(self, startPt, angle): #bereken de haakse lijnen op basis van de gesnapte feature length = 100 x1 = startPt.x() + length * sin(radians(angle)) y1 = startPt.y() + length * cos(radians(angle)) x2 = startPt.x() + length * sin(radians(angle + 180)) y2 = startPt.y() + length * cos(radians(angle + 180)) straal = self.parent.straal.value() test = QgsCircle(QgsPoint(startPt), straal) geom_cString = test.toCircularString() geom_from_curve = QgsGeometry(geom_cString) self.roundRubberBand.setToGeometry(geom_from_curve) self.snapLayer.append(self.roundRubberBand) #self.roundRubberBand.setToGeometry(geom_cString) self.perpRubberBand.addPoint(QgsPointXY(x1,y1)) self.perpRubberBand.addPoint(QgsPointXY(x2,y2)) self.perpRubberBand.show() #voeg de rubberband toe als mogelijke snap laag self.snapLayer.append(self.perpRubberBand) #als het een hoek betreft moeten er 2 haakse lijnen worden getekend if len(self.snapFeature) == 0 or self.snapFeature[2] == None: if self.perpRubberBand2 in self.snapLayer: self.snapLayer.remove(self.perpRubberBand2) try: if self.snapFeature[2] != None: x3 = startPt.x() + length * sin(radians(angle + 90)) y3 = startPt.y() + length * cos(radians(angle + 90)) x4 = startPt.x() + length * sin(radians(angle + 270)) y4 = startPt.y() + length * cos(radians(angle + 270)) self.perpRubberBand2.addPoint(QgsPointXY(x3,y3)) self.perpRubberBand2.addPoint(QgsPointXY(x4,y4)) self.perpRubberBand2.show() #voeg de rubberband toe als mogelijke snap laag self.snapLayer.append(self.perpRubberBand2) except: None #verwijder het laatste punt (backspace of delete) 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(self.bandType()) del self.capturedPoints[-1] #return captured points def getCapturedGeometry(self): snapAngle = None points = self.capturedPoints #voor lijn -> minimaal 2 punten if self.captureMode == CaptureTool.CAPTURE_LINE: if len(points) < 2: return None #voor polygoon -> minimaal 3 punten if self.captureMode == CaptureTool.CAPTURE_POLYGON: if len(points) < 3: return None # Close polygon door het eerste punt ook als laatste toe te voegen if self.captureMode == CaptureTool.CAPTURE_POLYGON: points.append(points[0]) #geeft zowel de punten als ook de hoek terug (niet nodig bij lijn en polygoon, maar uniformering ivm punten) self.onGeometryAdded(points, snapAngle)
def __init__(self, canvas, config=None): super(PolylineTool, self).__init__(canvas) self.logger = roam.utils.logger if not config: self.config = {} else: self.config = config self.geom = None self.minpoints = 2 self.editmode = False self.editvertex = None self.points = [] self.is_tracking = False self.canvas = canvas self.valid_color = QColor.fromRgb(32, 218, 45, 100) self.invalid_color = QColor.fromRgb(216, 26, 96, 100) self.startcolour = self.valid_color self.editcolour = self.valid_color self.band = RubberBand(self.canvas, QgsWkbTypes.LineGeometry, width=5, iconsize=20) self.band.setColor(self.startcolour) self.errorband = RubberBand(self.canvas, QgsWkbTypes.LineGeometry, width=5, iconsize=20) self.errorband.setColor(QColor.fromRgb(64, 64, 64, 90)) self.errorlocations = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.errorlocations.setColor(self.invalid_color) self.errorlocations.setIconSize(20) self.errorlocations.setIcon(QgsRubberBand.ICON_BOX) self.pointband = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointband.setColor(self.startcolour) self.pointband.setIconSize(20) self.capturing = False self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.captureaction = CaptureAction(self, "line", text="Digitize") self.captureaction.toggled.connect(self.update_state) self.trackingaction = GPSTrackingAction(self) self.endcaptureaction = EndCaptureAction(self) self.undoaction = UndoAction(self) self.undoaction.setEnabled(False) self.undoaction.triggered.connect(self.undo) self.trackingaction.setEnabled(False) self.trackingaction.toggled.connect(self.set_tracking) self.captureaction.toggled.connect(self.update_end_capture_state) self.trackingaction.toggled.connect(self.update_end_capture_state) self.endcaptureaction.setEnabled(False) self.endcaptureaction.triggered.connect(self.endcapture) self.gpscapture = GPSCaptureAction(self, "line") self.gpscapture.setText("Add Vertex") self.gpscapture.triggered.connect(self.add_vertex) self.timer = QTimer() GPS.gpsfixed.connect(self.update_tracking_button) GPS.gpsdisconnected.connect(self.update_tracking_button_disconnect) RoamEvents.selectioncleared.connect(self.selection_updated) RoamEvents.selectionchanged.connect(self.selection_updated) self.snapping = True self.active_color = self.startcolour
class PolylineTool(QgsMapToolEdit): mouseClicked = pyqtSignal(QgsPoint) geometryComplete = pyqtSignal(QgsGeometry) error = pyqtSignal(str) MODE = "POLYLINE" def __init__(self, canvas, config=None): super(PolylineTool, self).__init__(canvas) self.logger = roam.utils.logger if not config: self.config = {} else: self.config = config self.geom = None self.minpoints = 2 self.editmode = False self.editvertex = None self.points = [] self.is_tracking = False self.canvas = canvas self.valid_color = QColor.fromRgb(32, 218, 45, 100) self.invalid_color = QColor.fromRgb(216, 26, 96, 100) self.startcolour = self.valid_color self.editcolour = self.valid_color self.band = RubberBand(self.canvas, QgsWkbTypes.LineGeometry, width=5, iconsize=20) self.band.setColor(self.startcolour) self.errorband = RubberBand(self.canvas, QgsWkbTypes.LineGeometry, width=5, iconsize=20) self.errorband.setColor(QColor.fromRgb(64, 64, 64, 90)) self.errorlocations = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.errorlocations.setColor(self.invalid_color) self.errorlocations.setIconSize(20) self.errorlocations.setIcon(QgsRubberBand.ICON_BOX) self.pointband = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry) self.pointband.setColor(self.startcolour) self.pointband.setIconSize(20) self.capturing = False self.cursor = QCursor( QPixmap([ "16 16 3 1", " c None", ". c #FF0000", "+ c #FFFFFF", " ", " +.+ ", " ++.++ ", " +.....+ ", " +. .+ ", " +. . .+ ", " +. . .+ ", " ++. . .++", " ... ...+... ...", " ++. . .++", " +. . .+ ", " +. . .+ ", " ++. .+ ", " ++.....+ ", " ++.++ ", " +.+ " ])) self.captureaction = CaptureAction(self, "line", text="Digitize") self.captureaction.toggled.connect(self.update_state) self.trackingaction = GPSTrackingAction(self) self.endcaptureaction = EndCaptureAction(self) self.undoaction = UndoAction(self) self.undoaction.setEnabled(False) self.undoaction.triggered.connect(self.undo) self.trackingaction.setEnabled(False) self.trackingaction.toggled.connect(self.set_tracking) self.captureaction.toggled.connect(self.update_end_capture_state) self.trackingaction.toggled.connect(self.update_end_capture_state) self.endcaptureaction.setEnabled(False) self.endcaptureaction.triggered.connect(self.endcapture) self.gpscapture = GPSCaptureAction(self, "line") self.gpscapture.setText("Add Vertex") self.gpscapture.triggered.connect(self.add_vertex) self.timer = QTimer() GPS.gpsfixed.connect(self.update_tracking_button) GPS.gpsdisconnected.connect(self.update_tracking_button_disconnect) RoamEvents.selectioncleared.connect(self.selection_updated) RoamEvents.selectionchanged.connect(self.selection_updated) self.snapping = True self.active_color = self.startcolour def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.reset() if event.key() == Qt.Key_S: self.toggle_snapping() RoamEvents.snappingChanged.emit(self.snapping) def setSnapping(self, snapping): self.snapping = snapping def toggle_snapping(self): self.snapping = not self.snapping def selection_updated(self, *args): if self.editmode: self.reset() self.setEditMode(False, None, None) def update_end_capture_state(self, *args): if self.trackingaction.isChecked() and self.captureaction.isChecked(): self.endcaptureaction.setEnabled(self.capturing) self.undoaction.setEnabled(self.capturing) def update_tracking_button_disconnect(self): self.trackingaction.setEnabled(False) self.captureaction.toggle() def update_tracking_button(self, gps_fixed, info): self.trackingaction.setEnabled(gps_fixed) def update_state(self, toggled): if self.is_tracking: self.set_tracking(False) def set_tracking(self, enable_tracking): if not enable_tracking and not self.captureaction.isChecked(): # Some other tool is grabbing us so we will keep trakcing return if enable_tracking: self.captureaction.setIcon(QIcon(":/icons/pause")) self.captureaction.setText("Pause") gpsettings = roam.config.settings.get("gps", {}) config = gpsettings.get('tracking', {"time", 1}) if "time" in config: self.band.removeLastPoint() self.timer.timeout.connect(self.add_vertex) value = config['time'] value *= 1000 self.timer.start(value) self.capturing = True elif "distance" in config: self.band.removeLastPoint() value = config['distance'] self.add_point(GPS.position) GPS.gpsposition.connect(self.track_gps_location_changed) else: self.captureaction.setIcon(self.captureaction._defaulticon) self.captureaction.setText("Digitize") if self.capturing: point = self.pointband.getPoint( 0, self.pointband.numberOfVertices() - 1) if point: self.band.addPoint(point) try: self.timer.stop() self.timer.timeout.disconnect(self.add_vertex) except TypeError: pass try: GPS.gpsposition.disconnect(self.track_gps_location_changed) except TypeError: pass self.is_tracking = enable_tracking self.trackingaction.set_text(enable_tracking) @property def max_distance(self): gpsettings = roam.config.settings.get("gps", {}) config = gpsettings.get('tracking', {"time", 1}) value = config['distance'] return value def track_gps_location_changed(self, point, info): lastpoint = self.pointband.getPoint( 0, self.pointband.numberOfVertices() - 1) distance = point.sqrDist(lastpoint) if distance >= self.max_distance: self.add_point(point) @property def actions(self): return [ self.captureaction, self.trackingaction, self.gpscapture, self.endcaptureaction, self.undoaction ] def add_vertex(self): location = GPS.position self.remove_last_point() self.add_point(location) # Add an extra point for the move band self.band.addPoint(location) def remove_last_vertex(self): self.remove_last_point() self.band.removeLastPoint(doUpdate=True) def canvasPressEvent(self, event): geom = self.band.asGeometry() if not geom: return point = point_from_event(event, self.snapping) if self.editmode: layer = self.currentVectorLayer() event.snapToGrid(layer.geometryOptions().geometryPrecision(), layer.crs()) tol = QgsTolerance.vertexSearchRadius(self.canvas.mapSettings()) loc = self.canvas.snappingUtils().locatorForLayer(layer) matches = loc.verticesInRect(point, tol) if matches: for match in matches: if match.featureId() != self.feature.id(): continue self.editpart = 0 self.editvertex = match.vertexIndex() break else: self.editvertex = None self.editpart = 0 def vertex_at_point(self, point): layer = self.currentVectorLayer() tol = QgsTolerance.vertexSearchRadius(self.canvas.mapSettings()) loc = self.canvas.snappingUtils().locatorForLayer(layer) matches = loc.verticesInRect(point, tol) return matches def canvasReleaseEvent(self, event: QgsMapMouseEvent): if event.button() == Qt.RightButton: self.endcapture() return if not self.editmode: point = event.snapPoint() self.add_point(point) else: self.editvertex = None def canvasMoveEvent(self, event: QgsMapMouseEvent): if self.is_tracking: return point = point_from_event(event, self.snapping) if not self.editmode: self.pointband.movePoint(point) if self.capturing: self.band.movePoint(point) if self.editmode and self.editvertex is not None: found, vertexid = self.geom.vertexIdFromVertexNr(self.editvertex) self.geom.get().moveVertex(vertexid, QgsPoint(point)) self.currentVectorLayer().triggerRepaint() self.feature.setGeometry(self.geom) self.currentVectorLayer().updateFeature(self.feature) self.pointband.setToGeometry(self.toMultiPoint(self.geom), self.currentVectorLayer()) self.band.setToGeometry(self.geom, self.currentVectorLayer()) self.update_valid_state() @property def is_polygon_mode(self): """ Returns true if this tool is in polygon mode :note: This is a gross hack to avoid a import issue until it's refactored :return: """ return self.MODE == "POLYGON" def update_valid_state(self): geom = self.band.asGeometry() if geom and self.band.numberOfVertices() > 0: if self.has_errors(): self.set_band_color(self.invalid_color) else: if not self.editmode: self.set_band_color(self.startcolour) else: self.set_band_color(self.editcolour) @property def active_color(self): return self._active_color @active_color.setter def active_color(self, color): self._active_color = color def has_errors(self): if self.geom is None: geom = self.band.asGeometry() else: geom = self.geom errors = geom.validateGeometry() skippable = ["duplicate node", "Geometry has"] othererrors = [] def is_safe(message): for safe in skippable: if safe in message: return True return False # We need to remove errors that are "ok" and not really that bad # We are harder on what is considered valid for polygons. if self.is_polygon_mode: for error in errors: if not is_safe(error.what()): othererrors.append(error) if self.node_count < self.minpoints: error = QgsGeometry.Error("Number of nodes < {0}".format( self.minpoints)) othererrors.append(error) return othererrors @property def node_count(self): if self.editmode: return self.band.numberOfVertices() else: return self.band.numberOfVertices() - 1 def add_point(self, point): self.points.append(point) self.band.addPoint(point) self.pointband.addPoint(point) self.capturing = True self.endcaptureaction.setEnabled(True) self.undoaction.setEnabled(True) def remove_last_point(self): if len(self.points) > 1: del self.points[-1] self.band.removeLastPoint(doUpdate=True) self.pointband.removeLastPoint(doUpdate=True) def get_geometry(self): if self.geom is None: return self.band.asGeometry() else: return self.geom def endinvalidcapture(self, errors): self.errorband.setToGeometry(self.get_geometry(), self.currentVectorLayer()) for error in errors: if error.hasWhere(): self.errorlocations.addPoint(error.where()) self.capturing = False self.set_tracking(False) self.captureaction.setChecked(True) self.undoaction.setEnabled(False) self.endcaptureaction.setEnabled(False) self.reset() def undo(self): self.remove_last_point() def endcapture(self): if not self.editmode: self.band.removeLastPoint() errors = self.has_errors() if errors and self.config.get("geometry_validation", True): self.error.emit("Invalid geometry. <br>" "Please recapture. Last capture shown in grey <br>" "<h2>Errors</h2> {0}".format("<br>".join( error.what() for error in errors))) self.endinvalidcapture(errors) return self.capturing = False self.set_tracking(False) self.captureaction.setChecked(True) self.undoaction.setEnabled(False) self.endcaptureaction.setEnabled(False) self.clearErrors() self.geometryComplete.emit(self.get_geometry()) def clearErrors(self): self.errorband.reset(QgsWkbTypes.LineGeometry) self.errorlocations.reset(QgsWkbTypes.PointGeometry) def clearBand(self): self.reset() def reset(self, *args): self.band.reset(QgsWkbTypes.LineGeometry) self.pointband.reset(QgsWkbTypes.PointGeometry) self.capturing = False self.set_tracking(False) self.undoaction.setEnabled(False) self.endcaptureaction.setEnabled(False) def activate(self): self.pointband.reset(QgsWkbTypes.PointGeometry) self.pointband.addPoint(QgsPointXY(0, 0)) self.canvas.setCursor(self.cursor) def deactivate(self): """ Deactive the tool. """ self.clearBand() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def set_band_color(self, color): self.active_color = color self.band.setColor(color) self.pointband.setColor(color) def setEditMode(self, enabled, geom, feature): self.reset() self.editmode = enabled if self.geom != geom: self.clearErrors() self.geom = geom self.feature = feature if self.editmode: self.set_band_color(self.editcolour) self.pointband.setToGeometry(self.toMultiPoint(geom), self.currentVectorLayer()) self.pointband.setVisible(True) self.band.setToGeometry(geom, self.currentVectorLayer()) else: self.set_band_color(self.startcolour) self.endcaptureaction.setEnabled(self.editmode) self.endcaptureaction.setEditing(enabled) self.captureaction.setEditMode(enabled) def toMultiPoint(self, geom): points = QgsMultiPoint() for count, v in enumerate(geom.vertices()): points.addGeometry(v.clone()) newgeom = QgsGeometry(points) return newgeom
class SnapPointTool(QgsMapTool): def __init__(self, canvas): self.canvas = canvas QgsMapTool.__init__(self, canvas) self.layer = None self.snapping = False self.onGeometryAdded = None self.snapPt = None self.startRotate = False self.tempRubberBand = None self.vertexmarker = None self.snapLayer = [] self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): clickedPt = None drawPoint = None snapAngle = None if event.button() == Qt.LeftButton: #als er gesnapt wordt -> bereken rotatie op basis van geklikt punt en snappunt if self.snapPt != None: mapPt,clickedPt = self.transformCoordinates(event.pos()) drawPoint = self.toLayerCoordinates(self.layer, self.snapPt) snapAngle = clickedPt.azimuth(drawPoint) self.stopPointTool() #als er niets gesnapt wordt maar geroteerd bereken de rotatie op basis van de 2 punten in temprubberband elif self.startRotate: mapPt,clickedPt = self.transformCoordinates(event.pos()) tempGeometry = self.tempRubberBand.asGeometry().asPolyline() drawPoint = self.toLayerCoordinates(self.layer, tempGeometry[0]) snapAngle = drawPoint.azimuth(clickedPt) self.tempRubberBand.hide() self.stopPointTool() else: #als aan beide bovenstaande voorwaarden niet wordt voldaan wordt het pictogram gewoon geplaatst en is de hoek 0 mapPt,drawPoint = self.transformCoordinates(event.pos()) self.onGeometryAdded(drawPoint, snapAngle) self.snapPt = None #als de rechtermuisknop wordt gebruikt (1e keer) start het roteren if event.button() == Qt.RightButton: if not self.startRotate: self.start_to_rotate(event) #reset everything def stopPointTool(self): if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None if self.vertexmarker: self.canvas.scene().removeItem(self.vertexmarker) self.vertexmarker = None self.startRotate = False self.canvas.refresh() #indien roteren -> init temprubberband def start_to_rotate(self, event): mapPt,layerPt = self.transformCoordinates(event.pos()) color = QColor("black") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(4) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.tempRubberBand.addPoint(mapPt) self.startRotate = True def canvasMoveEvent(self, event): mapPt,layerPt = self.transformCoordinates(event.pos()) #indien er niet geroteerd wordt kan er worden gesnapt op vooraf gedfinieerde lagen if not self.startRotate: if self.snapping == True: self.snapPt = self.snap_to_point(event.pos(), layerPt) if self.vertexmarker != None: self.vertexmarker.hide() if self.snapPt != None: self.vertexmarker.setCenter(self.snapPt) self.vertexmarker.show() else: #indien geroteerd wordt - teken de rubberband self.tempRubberBand.movePoint(mapPt) def snap_to_point(self, pos, layerPt): snapPoints = [] distance = [] snapPoint = None val = 0 idx = None if self.vertexmarker == None: self.vertexmarker = QgsVertexMarker(self.canvas) self.vertexmarker.setColor(QColor(255, 0, 255)) self.vertexmarker.setIconSize(5) self.vertexmarker.setIconType(QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.vertexmarker.setPenWidth(5) self.vertexmarker.show() #berken snap toleratie en extent (rechthoek waarbinnen gesnapt kan worden) tolerance = pow(self.calcTolerance(pos),2) extent = self.calcExtent(layerPt, tolerance) #haal features op waarop gesnapt kan worden request = QgsFeatureRequest() request.setFilterRect(extent) request.setFlags(QgsFeatureRequest.ExactIntersect) for ilayer in self.snapLayer: try: index = None nearestId = None ifeature = None tuple = None #bereken voor elke snap laag de dichtsbijzinde feature index = QgsSpatialIndex(ilayer.getFeatures(request)) nearestId = index.nearestNeighbor(layerPt, 2)[0] ifeature = next(ilayer.getFeatures(QgsFeatureRequest(nearestId))) #bereken per feature het dichtsbijzijnde lijn segment tuple = ifeature.geometry().closestSegmentWithContext(layerPt) #als de afstand tot het lijnsegment kleiner is als de tolerantie voeg toe als snappunt if tuple[0] != None and tuple[0] < tolerance: distance.append(tuple[0]) snapPoints.append(tuple[1]) else: distance.append(tolerance + 1) snapPoints.append('') except: None #bereken het dichtsbijzijnde snappunt voor alle snap lagen if len(distance) > 0: val, idx = min((val, idx) for (idx, val) in enumerate(distance)) if val <= tolerance and val != 0: snapPoint = self.toMapCoordinates(ilayer, snapPoints[idx]) if snapPoint != None: return snapPoint else: return None #bereken snap extent def calcExtent(self, layerPt, tolerance): extent = QgsRectangle(layerPt.x() - tolerance, layerPt.y() - tolerance, layerPt.x() + tolerance, layerPt.y() + tolerance) return extent #bereken de tolerantie def calcTolerance(self, pos): pt1 = QPoint(pos.x(), pos.y()) pt2 = QPoint(pos.x() + 20, pos.y()) mapPt1,layerPt1 = self.transformCoordinates(pt1) mapPt2,layerPt2 = self.transformCoordinates(pt2) tolerance = layerPt2.x() - layerPt1.x() return tolerance #transormeer de coordinaten naar canvas en laag punten def transformCoordinates(self, canvasPt): return (self.toMapCoordinates(canvasPt), self.toLayerCoordinates(self.layer, canvasPt))
class MovePointTool(QgsMapToolIdentify): def __init__(self, canvas, layer): QgsMapToolIdentify.__init__(self, canvas) self.canvas = canvas self.setCursor(Qt.CrossCursor) self.layer = layer self.dragging = False self.fields = None self.onMoved = None self.point = None self.startRotate = False self.tempRubberBand = None self.vertexMarker = None def canvasPressEvent(self, event): #op welke feature wordt er geklikt found_features = self.identify(event.x(), event.y(), self.TopDownStopAtFirst, self.VectorLayer) #check type van de laag, het werkt alleen voor point layers type_check = check_layer_type(found_features[0].mLayer) self.idlayer = found_features[0].mLayer feature = found_features[0].mFeature self.fid = feature.id() #indien de linkesmuisnop wordt geklikt, het betreft een point layer en er is een feature gevonden -> verslepen if event.button() == Qt.LeftButton: if (len(found_features) > 0 and type_check == "Point"): self.dragging = True #init drag point self.vertexmarker = QgsVertexMarker(self.canvas) self.vertexmarker.setColor(QColor(0, 0, 255)) self.vertexmarker.setIconSize(5) self.vertexmarker.setIconType(QgsVertexMarker.ICON_X) self.vertexmarker.setPenWidth(3) self.vertexmarker.show() #anders doe niets else: self.dragging = False self.fid = None #indien de rechtermuisknop wordt geklikt -> roteren if event.button() == Qt.RightButton: if (len(found_features) > 0 and type_check == "Point"): if not self.startRotate: self.start_to_rotate(event) else: self.startRotate = False self.fid = None def start_to_rotate(self, event): mapPt, layerPt = self.transformCoordinates(event.pos()) #init tempRubberband indicating rotation color = QColor("black") color.setAlphaF(0.78) self.tempRubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) self.tempRubberBand.setWidth(4) self.tempRubberBand.setColor(color) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.tempRubberBand.addPoint(mapPt) self.startRotate = True def canvasMoveEvent(self, event): #als verslepen -> verplaats de indicatieve marker if self.dragging: mapPt, layerPt = self.transformCoordinates(event.pos()) self.point = layerPt self.vertexmarker.setCenter(mapPt) #als roteren -> teken de tempRubberband als lijn if self.startRotate: mapPt, layerPt = self.transformCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPt) #transformeer muis lokatie naar canvas punt en laag punt def transformCoordinates(self, canvasPt): return (self.toMapCoordinates(canvasPt), self.toLayerCoordinates(self.layer, canvasPt)) def canvasReleaseEvent(self, event): #als verslepen -> pas de geometry van de betreffende feature aan if self.dragging: self.vertexmarker.hide() geom = QgsGeometry.fromPointXY(self.point) self.idlayer.dataProvider().changeGeometryValues({self.fid: geom}) self.idlayer.commitChanges() self.idlayer.triggerRepaint() self.stop_moveTool() #als roteren -> pas de rotatie van de betreffende feature aan op basis van de loodrechte lijn tussen muisklik en bestaand punt if self.startRotate: self.tempRubberBand.hide() mapPt, clickedPt = self.transformCoordinates(event.pos()) tempGeometry = self.tempRubberBand.asGeometry().asPolyline() drawPoint = self.toLayerCoordinates(self.layer, tempGeometry[0]) field = self.idlayer.fields().indexOf("rotatie") rotation = drawPoint.azimuth(clickedPt) attrs = {field: rotation} self.idlayer.dataProvider().changeAttributeValues( {self.fid: attrs}) self.idlayer.commitChanges() self.idlayer.triggerRepaint() self.stop_moveTool() #reset rubberbands def stop_moveTool(self): if self.tempRubberBand != None: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None if self.vertexMarker != None: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None self.fid = None self.startRotate = False self.dragging = False self.onMoved() self.canvas.refresh()