def createRubberBand(self, geomType, alternativeBand=False): ''' Creates a rubber band with the color/line width from the QGIS settings. ''' settings = QSettings() rb = QgsRubberBand(self.canvas, geomType) rb.setWidth(settings.value("/Qgis/digitizing/line_width", 1)) color = QColor(settings.value("/Qgis/digitizing/line_color_red", 255), \ settings.value("/Qgis/digitizing/line_color_green", 0), \ settings.value("/Qgis/digitizing/line_color_blue", 0)) myAlpha = settings.value("/Qgis/digitizing/line_color_alpha", 200) / 255.0 if alternativeBand: myAlpha = myAlpha * 0.75 rb.setLineStyle(Qt.DotLine) if geomType == QgsWkbTypes.PolygonGeometry: color.setAlphaF(myAlpha) rb.setColor(color) rb.show() return rb
class PolygonDrawMapVisualisation(object): def __init__(self, canvas): self.canvas = canvas self.points = [] # temp layer for side profile trac self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setColor(Qt.red) self.rb.setFillColor(QColor(255, 0, 0, 64)) self.rb.setLineStyle(Qt.SolidLine) self.rb.setWidth(1) self.reset() def close(self): self.points = [] self.reset() # delete the rubberband we've been re-using self.canvas.scene().removeItem(self.rb) def show(self): self.rb.show() def hide(self): self.rb.hide() def add_point(self, point): self.points.append(point) self.rb.addPoint(point, True) self.rb.show() def reset(self): self.points = [] self.rb.reset(QgsWkbTypes.PolygonGeometry)
class MapTool(QgsMapTool): geometry_changed = pyqtSignal(QgsGeometry, bool) tool_deactivated = pyqtSignal() def __init__(self, canvas, cursorstyle=Qt.CrossCursor): self.canvas = canvas QgsMapTool.__init__(self, canvas) self.caller = self.sender() self.cursorStyle = cursorstyle self.active = False # get selection color selcolor = self.canvas.selectionColor() mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(), 40) self.rb = QgsRubberBand(self.canvas) self.rb.setStrokeColor(QColor(255, 0, 0, 40)) self.rb.setFillColor(mycolor) self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine)) self.rb.setWidth(2) def setCursorStyle(self): cursor = QCursor() cursor.setShape(self.cursorStyle) self.setCursor(cursor) def activate(self): self.caller.setChecked(True) self.setCursorStyle() def deactivate(self): self.canvas.scene().removeItem(self.rb) self.tool_deactivated.emit() self.caller.setChecked(False) QgsMapTool.deactivate(self) def setGeometry(self, geo): self.rb.setToGeometry(geo) def canvasReleaseEvent(self, mouseEvent): if mouseEvent.button() == Qt.LeftButton: if not self.active: self.active = True self.geometry_changed.emit(QgsGeometry(), False) self.rb.reset(QgsWkbTypes.LineGeometry) self.rb.addPoint(mouseEvent.mapPoint()) if self.rb.numberOfVertices() > 1: self.geometry_changed.emit(self.rb.asGeometry(), False) elif mouseEvent.button() == Qt.RightButton: if self.rb.numberOfVertices() > 2: self.active = False self.rb.removeLastPoint() geo = self.rb.asGeometry() self.geometry_changed.emit(geo, True) else: self.rb.reset(QgsWkbTypes.LineGeometry) def canvasMoveEvent(self, mouseEvent): if self.rb.numberOfVertices() > 1 and self.active: self.rb.removeLastPoint() self.rb.addPoint(mouseEvent.mapPoint()) pass
class MapTool(QgsMapTool): geometry_changed = pyqtSignal(QgsGeometry,bool) tool_deactivated = pyqtSignal() def __init__(self, canvas, cursorstyle = Qt.CrossCursor): self.canvas = canvas QgsMapTool.__init__(self, canvas) self.caller = self.sender() self.cursorStyle=cursorstyle self.active = False self.center=None # get selection color selcolor =self.canvas.selectionColor() mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(), 40) self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) self.rb.setStrokeColor(QColor(255, 0, 0, 40)) self.rb.setFillColor(mycolor) self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine)) self.rb.setWidth(2) def setCursorStyle(self): cursor = QCursor() cursor.setShape(self.cursorStyle) self.setCursor(cursor) def activate(self): self.caller.setChecked(True) self.setCursorStyle() def deactivate(self): self.canvas.scene().removeItem(self.rb) self.tool_deactivated.emit() self.caller.setChecked(False) QgsMapTool.deactivate(self) def setGeometry(self,geo): self.rb.setToGeometry(geo) def canvasReleaseEvent(self, mouseevent): if mouseevent.button() == Qt.LeftButton: self.active = False self.geometry_changed.emit(self.rb.asGeometry(),True) pass def canvasMoveEvent(self, mouseEvent): if self.active: cp = self.toMapCoordinates(mouseEvent.pos()) rec = QgsRectangle(self.center, cp) self.rb.setToGeometry(QgsGeometry.fromRect(rec)) self.geometry_changed.emit(self.rb.asGeometry(),True) pass def canvasPressEvent(self, e): if e.button() == Qt.LeftButton: self.active = True self.center = self.toMapCoordinates(e.pos()) self.geometry_changed.emit(QgsGeometry(),False) self.rb.reset() pass
def crearNuevoRubberLinea(self): nuevoRubber = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) nuevoRubber.setFillColor(QColor(0,0,0,0)) nuevoRubber.setStrokeColor(QColor(0,62,240,255)) nuevoRubber.setWidth(2) #penStyle = Qt.PenStyle() nuevoRubber.setLineStyle(Qt.PenStyle(3)) return nuevoRubber
def create_rubber_band(self): """ Creates a new rubber band. """ band = QgsRubberBand(self.canvas()) band.setColor(QColor("green")) band.setWidth(2) band.setLineStyle(Qt.DashLine) return band
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 )
def init_rubberband(color, lineStyle, alphaF, width, bandType, canvas): """initiate the rubberbands""" rubberBand = QgsRubberBand(canvas, bandType) rubberBand.setStrokeColor(color) color.setAlpha(alphaF) rubberBand.setFillColor(color) rubberBand.setLineStyle(lineStyle) rubberBand.setWidth(width) return rubberBand
def init_rubberband(styleName, canvas, rbType): """initiate the rubberbands""" rbStyle = RBSTYLES[styleName] rubberBand = QgsRubberBand(canvas, getWKBType(rbType)) rubberBand.setStrokeColor(rbStyle["strokecolor"]) rbStyle["fillcolor"].setAlpha(rbStyle["alphaF"]) rubberBand.setFillColor(rbStyle["fillcolor"]) rubberBand.setLineStyle(rbStyle["linestyle"]) rubberBand.setWidth(rbStyle["strokewidth"]) return rubberBand
class MapTool(QgsMapToolIdentify): geometry_changed = pyqtSignal(QgsGeometry, bool) tool_deactivated = pyqtSignal() def __init__(self, canvas, cursorstyle=Qt.CrossCursor): self.canvas = canvas QgsMapTool.__init__(self, canvas) self.caller = self.sender() self.cursorStyle = cursorstyle self.active = False # get selection color selcolor = self.canvas.selectionColor() mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(), 40) self.rb = QgsRubberBand(self.canvas) self.rb.setStrokeColor(QColor(255, 0, 0, 40)) self.rb.setFillColor(mycolor) self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine)) self.rb.setWidth(2) self.center = None self.cercle = 120 #circle of 30 segments def setCursorStyle(self): cursor = QCursor() cursor.setShape(self.cursorStyle) self.setCursor(cursor) def activate(self): self.caller.setChecked(True) self.setCursorStyle() def deactivate(self): self.canvas.scene().removeItem(self.rb) self.tool_deactivated.emit() self.caller.setChecked(False) QgsMapTool.deactivate(self) def setGeometry(self, geo): self.rb.setToGeometry(geo) def canvasReleaseEvent(self, mouseEvent): self.geometry_changed.emit(QgsGeometry(), False) if mouseEvent.button() == Qt.LeftButton: results = self.identify(mouseEvent.x(), mouseEvent.y(), self.ActiveLayer, self.VectorLayer) for res in results: if res.mFeature and res.mLayer: geo = res.mFeature.geometry() self.rb.setToGeometry(geo) self.geometry_changed.emit(geo, True) break pass
def _createRubberBand(self, geometryType, moveBand=False): settings = QSettings() rb = QgsRubberBand(self.canvas(), geometryType) rb.setWidth(int(settings.value('/qgis/digitizing/line_width', 1))) color = QColor(int(settings.value('/qgis/digitizing/line_color_red', 255)), int(settings.value('/qgis/digitizing/line_color_green', 0)), int(settings.value('/qgis/digitizing/line_color_blue', 0))) myAlpha = int(settings.value('/qgis/digitizing/line_color_alpha', 200)) / 255.0 if (moveBand): myAlpha = myAlpha * float(settings.value('/qgis/digitizing/line_color_alpha_scale', 0.75)) rb.setLineStyle(Qt.DotLine) if (geometryType == QGis.Polygon): color.setAlphaF(myAlpha) color.setAlphaF(myAlpha) rb.setColor(color) rb.show() return rb
def visible_aoi(self): # first clean all rubber bands [rubber_band.reset() for rubber_band in self.rubber_bands] [rubber_band.reset() for rubber_band in self.tmp_rubber_bands] self.rubber_bands = [] self.tmp_rubber_bands = [] if self.VisibleAOI.isChecked() and self.aoi_features is not None: for feat in self.aoi_features.getFeatures(): color = QColor("red") color.setAlpha(70) rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) rubber_band.setToGeometry(feat.geometry()) rubber_band.setColor(color) rubber_band.setWidth(3) self.rubber_bands.append(rubber_band) tmp_rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry) tmp_rubber_band.setToGeometry(feat.geometry()) tmp_rubber_band.setColor(color) tmp_rubber_band.setWidth(3) tmp_rubber_band.setLineStyle(Qt.DotLine) self.tmp_rubber_bands.append(tmp_rubber_band)
def __init__(self, id, gtotool, config, debug): super(run, self).__init__() self.gtomain = gtotool.gtomain self.debug = debug self.gtotool = gtotool self.iface = self.gtotool.iface self.canvas = self.iface.mapCanvas() try: # get metadata sourcelayer = QgsProject.instance().mapLayersByName(config["sourcelayer"])[0] targetlayer = QgsProject.instance().mapLayersByName(config['targetlayer'])[0] _fields = config['fields'] self.iface.setActiveLayer(targetlayer) if not targetlayer.isEditable(): targetlayer.startEditing() # save current tool prevTool = self.canvas.mapTool() # create tool curTool = IdentifyTool(self.canvas) # QgsMapToolIdentifyFeatur(iface.mapCanvas()) curTool.setLayer = sourcelayer # curTool.featureIdentified.connect(self.feature_Identified) # create rubberband myrubber = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry) myrubber.setColor(QColor(Qt.blue)) myrubber.setLineStyle(Qt.PenStyle(Qt.DashDotLine)) myrubber.setWidth(2) def showrubberband(currentPos): if myrubber and myrubber.numberOfVertices(): myrubber.removeLastPoint() myrubber.addPoint(currentPos) self.canvas.xyCoordinates.connect(showrubberband) # references self.sourcefeat = None self.feat = None self.lyr = None self.rubbers = [] def on_click(mouseEvent, lyr, feat): try: if debug and feat.isValid(): gtotool.info.log("identified: layer", lyr.name(), "feat-ID:", feat.id()) else: gtotool.info.log("clicked:", mouseEvent.mapPoint()) if myrubber.numberOfVertices() < 2: if feat.isValid(): self.sourcefeat = feat sourcelayer.selectByIds([self.sourcefeat.id()]) myrubber.addPoint(mouseEvent.mapPoint()) curTool.layers = [targetlayer] else: if mouseEvent.button() == Qt.LeftButton: if feat.isValid() and feat.id() != self.sourcefeat.id(): try: targetlayer.selectByIds([feat.id()], QgsVectorLayer.AddToSelection) self.canvas.flashFeatureIds(targetlayer, [feat.id()]) provider = targetlayer.dataProvider() # QgsVectorDataProvider fields = provider.fields() # QMap<int, QgsField> targetlayer.beginEditCommand("attribute transfer") for fsource, ftarget in _fields.items(): feat[ftarget] = fields.field(ftarget).convertCompatible( self.sourcefeat[fsource]) # QgsField targetlayer.updateFeature(feat) targetlayer.endEditCommand() except Exception as e: gtotool.info.err(e) targetlayer.destroyEditCommand() elif mouseEvent.button() == Qt.RightButton: try: targetlayer.endEditCommand() myrubber.removeLastPoint() resetscreen() self.canvas.refresh() curTool.layers = [sourcelayer] except Exception as e: gtotool.info.err(e) targetlayer.destroyEditCommand() except Exception as e: gtotool.info.err(e) def resetscreen(): for r in self.rubbers: self.canvas.scene().removeItem(r) def tool_changed(tool): # another tool was activated self.canvas.xyCoordinates.disconnect(showrubberband) self.canvas.mapToolSet.disconnect(tool_changed) self.gtotool.action.setChecked(False) self.canvas.scene().removeItem(myrubber) resetscreen() curTool.geomIdentified.connect(on_click) self.canvas.setMapTool(curTool) self.gtotool.action.setCheckable(True) self.gtotool.action.setChecked(True) self.canvas.mapToolSet.connect(tool_changed) if not debug: targetlayer.commitChanges() sourcelayer.commitChanges() except Exception as e: gtotool.info.err(e)
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 GPS(QObject): def __init__(self, gtomain): super(GPS, self).__init__() self.gtomain = gtomain self.debug = self.gtomain.debug self.helper = self.gtomain.helper self.iface = self.gtomain.iface self.info = self.gtomain.info self.prj = None self.canvas = self.iface.mapCanvas() self.gpsLog = Info(self) self.gpsLog.panel_name = "GTO-GPS" self.mouse = None self.LastMapTool = None try: # settings self.settings = None self.timer_intervall = 1000 self.port = None self.pdop = 0 self.wgs_crs = 'EPSG:4326' self.gps_streaming_distance = 10 # refs self.gpsCon = None self.gpsDetector = None self.gps_active = False self.dic_gpsinfo = {} self.prj_crs = None self.src_crs = None self.transformation = None self.marker = None self.prevPointXY = None self.prevTime = None self.lastGpsInfo = None self.center = False self.scale = 0 # actions mw = self.iface.mainWindow() self.actionGPStoggle = QAction("GTO-GPS", mw) self.actionGPStoggle.setObjectName('mActionGTOgpsToggle') self.actionGPStoggle.setToolTip('GTO GPS starten') self.actionGPStoggle.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsToggle.png')) self.actionGPStoggle.setCheckable(True) self.actionGPStoggle.setChecked(False) self.actionGPStoggle.toggled.connect(self.activate) self.actionGPSaddPoint = QAction("GPS Punkt hinzufügen", mw) self.actionGPSaddPoint.setObjectName('mActionGTOgpsAddPoint') self.actionGPSaddPoint.setToolTip('GPS Punkt hinzufügen') self.actionGPSaddPoint.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsAddPoint.png')) self.actionGPSaddPoint.triggered.connect(self.addPoint) self.actionGPSaddPoint.setEnabled(False) self.actionGPSclick = QAction("GPS Punkt hinzufügen", mw) self.actionGPSclick.setObjectName('mActionGTOgpsAddcPoint') self.actionGPSclick.setToolTip('GPS Punkt hinzufügen') self.actionGPSclick.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsAddcPoint.png')) self.actionGPSclick.triggered.connect(self.gps_click) self.actionGPSclick.setEnabled(False) self.actionGPSstreaming = QAction("GPS streaming", mw) self.actionGPSstreaming.setObjectName('mActionGTOgpsStream') self.actionGPSstreaming.setToolTip('GPS Punkte aufzeichnen') self.actionGPSstreaming.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsStream.png')) self.actionGPSstreaming.setCheckable(True) self.actionGPSstreaming.setEnabled(False) self.actionGPSstreaming.toggled.connect(self.streaming) self.actionGPSsave = QAction("GPS Geometrie speichern", mw) self.actionGPSsave.setObjectName('mActionGTOgpsSave') self.actionGPSsave.setToolTip('GPS Geometrie speichern') self.actionGPSsave.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsSave.png')) self.actionGPSsave.triggered.connect(self.saveGeometry) self.actionGPSsave.setEnabled(False) self.actionGPSaddVertexTool = QAction("GPS AddVertexTool", mw) self.actionGPSaddVertexTool.setObjectName( 'mActionGTOgpsAddVertexTool') self.actionGPSaddVertexTool.setToolTip('GPS add vertex to stream') self.actionGPSaddVertexTool.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsAddVertexTool.png')) self.actionGPSaddVertexTool.setCheckable(True) self.actionGPSaddVertexTool.toggled.connect( self.activateVertexTool) self.actionGPSaddVertexTool.setEnabled(False) # add vertex tool self.streamTool = VertexTool(self.iface, self.canvas, True) self.streamTool.canvasReleased.connect(self.addVertex) self.streamTool.isActive.connect(self.tool_isactive) self.actionGPSlog = QAction("GPS Log", mw) self.actionGPSlog.setObjectName('mActionGTOgpsLog') self.actionGPSlog.setToolTip('GPS events anzeigen') self.actionGPSlog.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsLog.png')) self.actionGPSlog.setCheckable(True) self.actionGPScenter = QAction("GPS zentriere Karte", mw) self.actionGPScenter.setObjectName('mActionGTOgpsCenter') self.actionGPScenter.setToolTip('GPS zentriere Karte') self.actionGPScenter.setIcon( self.gtomain.helper.getIcon('mActionGTOgpsCenter.png')) self.actionGPScenter.setCheckable(True) self.actionGPScenter.toggled.connect(self.activateCenter) self.actionGPSport = GtoWidgetGpsPort(self, mw) self.actionGPSport.portChanged.connect(self.setPort) self.canvas.currentLayerChanged.connect(self.layer_changed) # streaming self.debugXoffset = 0 # debugging self.debugYoffset = 0 # rubberband # get selection color selcolor = self.canvas.selectionColor() mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(), 40) self.rb = QgsRubberBand(self.canvas) self.rb.setStrokeColor(QColor(255, 0, 0, 90)) self.rb.setFillColor(mycolor) self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine)) self.rb.setWidth(4) # watchdog self.watchdog = QTimer() self.watchdog.setInterval(2000) self.watchdog.timeout.connect(self.watch_dog) # layer self.streamLayer = None self.pointLayer = None self.gps_stream_mode = 0 # "0=rubber, 1=click, 2=both self.gps_debug = False except Exception as e: self.info.err(e) def watch_dog(self): try: self.watchdog.stop() self.gpsLog.log('Status:', self.gpsCon.status()) # doesnt work properly self.marker.setColor(QColor(255, 0, 0)) if self.actionGPSlog.isChecked(): self.gpsLog.log('No signal!') res = self.info.gtoQuestion("GPS deaktivieren? (Empfohlen)", title='Kein GPS Signal!', btns=QMessageBox.Yes | QMessageBox.No, parent=self.iface.mainWindow()) if res == QMessageBox.Yes: self.deactivate() self.actionGPStoggle.setChecked(False) except Exception as e: self.info.err(e) def init(self): try: if self.debug: self.gpsLog.log('gps init') self.actionGPStoggle.setChecked(False) # qgis self.prj = QgsProject().instance() self.canvas = self.iface.mapCanvas() # settings if "GPS" in self.gtomain.settings: # compatible self.settings = self.gtomain.settings["GPS"] else: self.settings = self.gtomain.settings # timer self.timer_intervall = self.settings.get("gps_timer_intervall", 5000) # gps self.port = self.settings.get("gps_port", None) if self.port is None: self.setPort(self.helper.getGlobalSetting('GpsPort')) self.pdop = self.settings.get("gps_pdop", 0) self.actionGPSlog.setChecked(self.settings.get("gps_log", False)) watchdog_interval = self.settings.get("gps_watchdog_interval", 3000) self.watchdog.setInterval(watchdog_interval) # streaming self.pointLayer = self.settings.get("gps_point_layer", None) try: if self.pointLayer is not None: self.pointLayer = self.prj.mapLayersByName( self.pointLayer)[0] geoType = self.pointLayer.geometryType() if geoType != QgsWkbTypes.GeometryType.PointGeometry: self.pointLayer = None except Exception as e: self.pointLayer = None self.info.err(e) self.gps_debug = self.settings.get("gps_debug", False) self.gps_stream_mode = self.settings.get("gps_stream_mode", 0) self.gps_streaming_distance = self.settings.get( "gps_streaming_distance", 1) # map self.center = self.settings.get("gps_center", True) self.actionGPScenter.setChecked(self.center) self.scale = self.settings.get("gps_scale", 0) # transformation self.prj_crs = self.prj.crs() self.wgs_crs = self.settings.get("gps_wgs_crs", 'EPSG:4326') self.init_transformation(self.wgs_crs) except Exception as e: self.info.err(e) def activateCenter(self): self.center = self.actionGPScenter.isChecked() def activate(self): try: self.deactivate() if self.actionGPStoggle.isChecked(): if self.port is None: self.info.msg("Kein GPS Port gesetzt!") return self.actionGPSport.setEnabled(False) if self.actionGPSlog.isChecked(): self.gpsLog.log('gps activate') self.gpsDetector = QgsGpsDetector(self.port) self.gpsDetector.detected[QgsGpsConnection].connect( self.connection_succeed) # self.gpsDetector.detected[QgsNmeaConnection].connect(self.connection_succeed) self.gpsDetector.detectionFailed.connect( self.connection_failed) self.gpsDetector.advance() self.init_marker() except Exception as e: self.info.err(e) def setPort(self, port): try: self.port = port self.actionGPStoggle.setToolTip('GPS on/off (Port:{0})'.format( self.port)) except Exception as e: self.info.err(e) def enableGPSfunctions(self, enabled): try: if self.iface.mainWindow() is None: return # dummy except: # prevent: ERROR: <class 'RuntimeError'> gto_gps.py | line: 240 | ('wrapped C/C++ object of type QgisInterface has been deleted',) # because QGIS is already closing return try: if self.iface.activeLayer() is not None: geoType = self.iface.activeLayer().geometryType() if geoType == QgsWkbTypes.GeometryType.PointGeometry: self.actionGPSaddPoint.setEnabled(enabled) else: self.actionGPSaddPoint.setEnabled(False) self.actionGPSclick.setEnabled(enabled) self.actionGPSstreaming.setEnabled(enabled) self.actionGPSport.setEnabled(not enabled) except Exception as e: self.info.err(e) def connection_succeed(self, connection): try: self.gps_active = True self.gpsCon = connection self.gpsCon.stateChanged.connect(self.status_changed) self.watchdog.start() except Exception as e: self.info.err(e) def connection_failed(self): if not self.gps_active: self.actionGPStoggle.setChecked(False) self.info.msg("GPS konnte nicht initialisiert werden!") self.info.log('GPS connection failed') self.enableGPSfunctions(False) def deactivate(self): try: if self.iface.mainWindow() is None: return # dummy except: # prevent: ERROR: <class 'RuntimeError'> gto_gps.py | line: 240 | ('wrapped C/C++ object of type QgisInterface has been deleted',) # because QGIS is already closing return try: if self.debug: self.info.log('gps deactivate') self.watchdog.stop() self.debugXoffset = 0 self.debugYoffset = 0 self.prevTime = None self.actionGPSstreaming.setChecked(False) self.enableGPSfunctions(False) self.prevPointXY = None if self.gpsCon is not None: if self.actionGPSlog.isChecked(): self.gpsLog.log('gps deactivate') self.gpsCon.close() if self.canvas is not None: self.canvas.scene().removeItem(self.marker) self.gps_active = False except Exception as e: self.info.err(e) def init_marker(self): try: if self.actionGPSlog.isChecked(): self.info.log('gps init_marker') self.marker = QgsVertexMarker(self.canvas) self.marker.setColor(QColor(255, 0, 0)) # (R,G,B) self.marker.setIconSize(10) self.circle = QgsVertexMarker.ICON_CIRCLE self.marker.setIconType(self.circle) # ICON_BOX # ICON_CIRCLE # ICON_CROSS # ICON_DOUBLE_TRIANGLE # ICON_NONE # ICON_X self.marker.setPenWidth(3) except Exception as e: self.info.err(e) def init_transformation(self, wgs_crs): try: self.src_crs = QgsCoordinateReferenceSystem(wgs_crs) self.transformation = QgsCoordinateTransform( self.src_crs, self.prj_crs, self.prj) except Exception as e: self.info.err(e) def status_changed(self, gpsInfo): try: if self.gpsCon.status() != 3: return # 3=data received self.watchdog.stop() self.watchdog.start() if gpsInfo.longitude == 0: return valid = False self.dic_gpsinfo = { "direction": gpsInfo.direction, "elevation": gpsInfo.elevation, "fixMode": gpsInfo.fixMode, "fixType": gpsInfo.fixType, "hacc": gpsInfo.hacc, "satInfoComplete": gpsInfo.satInfoComplete, "speed": gpsInfo.speed, "utcDateTime": gpsInfo.utcDateTime, "vacc": gpsInfo.vacc, "status": gpsInfo.status, "hdop": gpsInfo.hdop, "vdop": gpsInfo.vdop, "pdop": gpsInfo.pdop, "latitude": gpsInfo.latitude, "longitude": gpsInfo.longitude, "satellitesInView": gpsInfo.satellitesInView, "satellitesUsed": gpsInfo.satellitesUsed, "quality": gpsInfo.quality } if self.actionGPSlog.isChecked(): self.gpsLog.log('direction:', gpsInfo.direction) self.gpsLog.log('elevation:', gpsInfo.elevation) self.gpsLog.log('fixMode:', gpsInfo.fixMode) self.gpsLog.log('fixType:', gpsInfo.fixType) self.gpsLog.log('hacc:', gpsInfo.hacc) self.gpsLog.log('satInfoComplete:', gpsInfo.satInfoComplete) self.gpsLog.log('speed:', gpsInfo.speed) self.gpsLog.log('utcDateTime:', gpsInfo.utcDateTime.toString()) self.gpsLog.log('vacc:', gpsInfo.vacc) self.gpsLog.log('status:', gpsInfo.status) self.gpsLog.log('hdop:', gpsInfo.hdop) self.gpsLog.log('vdop:', gpsInfo.vdop) self.gpsLog.log('pdop:', gpsInfo.pdop) self.gpsLog.log('latitude:', gpsInfo.latitude) self.gpsLog.log('longitude:', gpsInfo.longitude) self.gpsLog.log('satellitesInView:', len(gpsInfo.satellitesInView)) self.gpsLog.log('satellitesUsed:', gpsInfo.satellitesUsed) self.gpsLog.log('quality:', gpsInfo.quality) self.gpsLog.log("---------------------") if gpsInfo.pdop >= self.pdop: # gps ok valid = True self.enableGPSfunctions(True) self.marker.setColor(QColor(0, 200, 0)) else: self.enableGPSfunctions(False) self.marker.setColor(QColor(255, 0, 0)) wgs84_pointXY = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude) wgs84_point = QgsPoint(wgs84_pointXY) wgs84_point.transform(self.transformation) if self.gps_debug: self.debugXoffset = self.debugXoffset + random.randint( 0, int(self.gps_streaming_distance)) self.debugYoffset = self.debugYoffset + random.randint( 0, int(self.gps_streaming_distance)) x = wgs84_point.x() + self.debugXoffset y = wgs84_point.y() + self.debugYoffset else: x = wgs84_point.x() y = wgs84_point.y() if self.actionGPSlog.isChecked(): self.gpsLog.log("x:", x) self.gpsLog.log("y:", y) self.gpsLog.log("--------------------") mapPointXY = QgsPointXY(x, y) self.lastGpsInfo = gtoGPSinfo(gpsInfo, mapPointXY, valid) # map if self.center and valid: self.canvas.setCenter(mapPointXY) if self.scale > 0: self.canvas.zoomScale(self.scale) self.marker.setCenter(mapPointXY) self.marker.show() # streaming gpsDateTime = None diff = 0 if self.pointLayer is not None: self.actionGPSaddPoint.setEnabled(valid) if self.actionGPSstreaming.isChecked( ) and valid and not self.actionGPSaddVertexTool.isChecked(): if self.prevTime is None: self.prevTime = gpsInfo.utcDateTime.currentDateTime() else: gpsDateTime = gpsInfo.utcDateTime.currentDateTime() diff = self.prevTime.msecsTo(gpsDateTime) if diff < self.timer_intervall: return if self.prevPointXY is None: self.prevPointXY = mapPointXY self.prevTime = gpsDateTime if self.pointLayer is not None: self.addPoint() else: if self.gps_stream_mode == 1: self.gps_click() elif self.gps_stream_mode == 2: self.gps_click() self.addVertex(mapPointXY) else: self.addVertex(mapPointXY) else: qgsDistance = QgsDistanceArea() distance = qgsDistance.measureLine( [self.prevPointXY, mapPointXY]) if distance > self.gps_streaming_distance and diff > self.timer_intervall: self.prevTime = gpsDateTime self.prevPointXY = mapPointXY if self.pointLayer is not None: self.addPoint() else: if self.gps_stream_mode == 1: self.gps_click() elif self.gps_stream_mode == 2: self.gps_click() self.addVertex(mapPointXY) else: self.addVertex(mapPointXY) except Exception as e: self.info.err(e) def gps_click(self): from pynput.mouse import Button, Controller try: if self.gps_active: if self.debug: self.gpsLog.log('gps_click') actionAddFeature = self.iface.mainWindow().findChild( QAction, 'mActionAddFeature') gtoGpsInfo = self.lastGpsInfo if gtoGpsInfo.isValid and actionAddFeature.isChecked(): self.mouse = Controller() mapX = gtoGpsInfo.mapPointXY.x() mapY = gtoGpsInfo.mapPointXY.y() pointXY = self.canvas.getCoordinateTransform().transform( mapX, mapY) # map coords to device coords x = pointXY.x() y = pointXY.y() p = self.canvas.mapToGlobal(QPoint( x, y)) # device coords to screen coords x = p.x() y = p.y() prevX, prevY = self.mouse.position self.mouse.position = (x, y) if self.debug: self.gpsLog.log('addPoint', x, "/", y) self.mouse.click(Button.left, 1) self.mouse.position = (prevX, prevY) except Exception as e: self.info.err(e) def layer_changed(self): try: self.actionGPSstreaming.setChecked(False) if self.gps_active: geoType = self.iface.activeLayer().geometryType() if geoType == QgsWkbTypes.GeometryType.PointGeometry or self.pointLayer is not None: self.actionGPSaddPoint.setEnabled(True) else: self.actionGPSaddPoint.setEnabled(False) except Exception as e: self.info.err(e) def streaming(self): try: self.actionGPSsave.setEnabled(False) self.actionGPSaddVertexTool.setChecked(False) self.activateVertexTool() if self.pointLayer is not None: return if self.actionGPSstreaming.isChecked(): if self.streamLayer is not None: self.actionGPSstreaming.setChecked(False) res = self.saveGeometry() if res == QMessageBox.Cancel: return self.actionGPSstreaming.setChecked(True) geoType = self.iface.activeLayer().geometryType() if geoType == QgsWkbTypes.GeometryType.PolygonGeometry or geoType == QgsWkbTypes.GeometryType.LineGeometry: self.streamLayer = self.iface.activeLayer() self.actionGPSaddVertexTool.setEnabled(True) self.rb.reset(geoType) else: self.actionGPSstreaming.setChecked(False) self.actionGPSaddVertexTool.setEnabled(False) self.info.msg( "Aktiver Layer muss vom Typ Polgone oder Line sein!") else: self.actionGPSaddVertexTool.setEnabled(False) if self.streamLayer is not None: geoType = self.streamLayer.geometryType() tools = self.settings.get('gps_afterstream_tools', []) if geoType == QgsWkbTypes.GeometryType.PolygonGeometry: if self.rb.numberOfVertices() > 2: self.actionGPSsave.setEnabled(True) self.gtomain.runcmd(tools) if geoType == QgsWkbTypes.GeometryType.LineGeometry: if self.rb.numberOfVertices() > 1: self.actionGPSsave.setEnabled(True) self.gtomain.runcmd(tools) except Exception as e: self.info.err(e) def addPoint(self): try: if self.gps_active: if self.actionGPSlog.isChecked(): self.gpsLog.log('addPoint') gtoGpsInfo = self.lastGpsInfo if gtoGpsInfo.isValid: if self.pointLayer is not None: layer = self.pointLayer else: layer = self.iface.activeLayer() geoType = layer.geometryType() if geoType != QgsWkbTypes.GeometryType.PointGeometry: self.info.msg("Kein Punktlayer ausgewählt!") return # create feature feat = QgsVectorLayerUtils.createFeature(layer) tr = QgsCoordinateTransform(self.prj_crs, layer.crs(), self.prj) tr_point = tr.transform(gtoGpsInfo.mapPointXY) geo = QgsGeometry.fromPointXY(tr_point) feat.setGeometry(geo) # set attributes gps_addpoint_attributes = self.settings.get( "gps_addpoint_attributes", {}) for k, v in gps_addpoint_attributes.items(): feat[k] = layer.fields().field(k).convertCompatible( self.dic_gpsinfo[v]) # add to layer if self.pointLayer is not None: # add to provider (res, outFeats) = layer.dataProvider().addFeatures([feat]) layer.select(outFeats[0].id()) else: if not layer.isEditable(): layer.startEditing() layer.beginEditCommand('Add stream geometry') layer.addFeatures([feat]) layer.endEditCommand() self.helper.refreshLayer(layer) except Exception as e: self.info.err(e) def saveGeometry(self, force=False): try: if self.streamLayer is not None: if not force: res = self.info.gtoQuestion( "GPS Stream-Geometry in Layer \n{0}\nspeichern?". format(self.streamLayer.name()), title='GPS Streaming', btns=QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, parent=self.iface.mainWindow()) if res == QMessageBox.Cancel: return res if res == QMessageBox.No: self.actionGPSsave.setEnabled(False) self.streamLayer = None self.rb.reset() return # create feature feat = QgsVectorLayerUtils.createFeature(self.streamLayer) geo = self.rb.asGeometry() feat.setGeometry(geo) # add to layer if not self.streamLayer.isEditable(): self.streamLayer.startEditing() self.streamLayer.beginEditCommand('Add stream geometry') self.streamLayer.addFeatures([feat]) self.streamLayer.endEditCommand() # add to provider # (res, outFeats) = self.streamLayer.dataProvider().addFeatures([feat]) # self.streamLayer.select(outFeats[0].id()) # reset streaming self.actionGPSsave.setEnabled(False) self.streamLayer = None self.rb.reset() except Exception as e: self.streamLayer.destroyEditCommand() self.info.err(e) def addVertex(self, point): try: self.rb.addPoint(point) except Exception as e: self.info.err(e) def activateVertexTool(self): try: if self.actionGPSaddVertexTool.isChecked(): if self.actionGPSlog.isChecked(): self.gpsLog.log('activated VertexTool: stream paused') self.LastMapTool = self.canvas.mapTool() self.canvas.setMapTool(self.streamTool) else: if self.actionGPSlog.isChecked(): self.gpsLog.log('deactivated VertexTool: stream resumed') self.canvas.setMapTool(self.LastMapTool) except Exception as e: self.info.err(e) def tool_isactive(self, active): try: pass except Exception as e: self.info.err(e)
class LinkerDock(QDockWidget, Ui_linker, SettingDialog): def __init__(self, iface): # QGIS self.iface = iface self.settings = MySettings() self.linkRubber = QgsRubberBand(self.iface.mapCanvas()) self.featureHighlight = None # Relation management self.relationManager = QgsProject.instance().relationManager() self.relationManager.changed.connect(self.loadRelations) self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationWidgetWrapper = None self.editorContext = QgsAttributeEditorContext() self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools()) # GUI QDockWidget.__init__(self) self.setupUi(self) SettingDialog.__init__(self, MySettings(), False, True) self.drawButton.setChecked(self.settings.value("drawEnabled")) self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas()) self.mapTool.setButton(self.identifyReferencingFeatureButton) # Connect signal/slot self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged) self.mapTool.featureIdentified.connect(self.setReferencingFeature) # load relations at start self.loadRelations() def showEvent(self, QShowEvent): self.drawLink() def closeEvent(self, e): self.iface.mapCanvas().unsetMapTool(self.mapTool) self.linkRubber.reset() self.deleteHighlight() self.deleteWrapper() self.disconnectLayer() def disconnectLayer(self): if self.relation.isValid(): self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside) def runForFeature(self, relationId, layer, feature): index = self.relationComboBox.findData(relationId) self.relationComboBox.setCurrentIndex(index) self.setReferencingFeature(feature) self.show() if not layer.isEditable(): self.iface.messageBar().pushMessage("Link It", "Cannot set a new related feature since %s is not editable" % layer.name(), QgsMessageBar.WARNING, 4) else: self.relationReferenceWidget.mapIdentification() @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked") def activateMapTool(self): self.iface.mapCanvas().setMapTool(self.mapTool) def deactivateMapTool(self): self.iface.mapCanvas().unsetMapTool(self.mapTool) def loadRelations(self): self.deleteWrapper() self.disconnectLayer() self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationComboBox.currentIndexChanged.disconnect(self.currentRelationChanged) self.relationComboBox.clear() for relation in self.relationManager.referencedRelations(): if relation.referencingLayer().hasGeometryType(): self.relationComboBox.addItem(relation.name(), relation.id()) self.relationComboBox.setCurrentIndex(-1) self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged) self.currentRelationChanged(-1) def currentRelationChanged(self, index): # disconnect previous relation if self.relation.isValid(): try: self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside) except TypeError: pass self.referencingFeatureLayout.setEnabled(index >= 0) relationId = self.relationComboBox.itemData(index) self.relation = self.relationManager.relation(relationId) self.mapTool.setLayer(self.relation.referencingLayer()) self.setReferencingFeature() # connect if self.relation.isValid(): self.relation.referencingLayer().editingStarted.connect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.connect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.connect(self.layerValueChangedOutside) def setReferencingFeature(self, feature=QgsFeature()): self.deactivateMapTool() self.referencingFeature = QgsFeature(feature) self.deleteWrapper() # disable relation reference widget if no referencing feature self.referencedFeatureLayout.setEnabled(feature.isValid()) # set line edit if not self.relation.isValid() or not feature.isValid(): self.referencingFeatureLineEdit.clear() return self.referencingFeatureLineEdit.setText("%s" % feature.id()) fieldIdx = self.referencingFieldIndex() widgetConfig = self.relation.referencingLayer().editorWidgetV2Config(fieldIdx) self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create("RelationReference", self.relation.referencingLayer(), fieldIdx, widgetConfig, self.relationReferenceWidget, self, self.editorContext) self.relationWidgetWrapper.setEnabled(self.relation.referencingLayer().isEditable()) self.relationWidgetWrapper.setValue(feature[fieldIdx]) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) # override field definition to allow map identification self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) # update drawn link self.highlightReferencingFeature() self.drawLink() def deleteWrapper(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged) self.relationWidgetWrapper.setValue(None) del self.relationWidgetWrapper self.relationWidgetWrapper = None def foreignKeyChanged(self, newKey): if not self.relation.isValid() or not self.relation.referencingLayer().isEditable() or not self.referencingFeature.isValid(): self.drawLink() return if not self.relation.referencingLayer().editBuffer().changeAttributeValue(self.referencingFeature.id(), self.referencingFieldIndex(), newKey): self.iface.messageBar().pushMessage("Link It", "Cannot change attribute value.", QgsMessageBar.CRITICAL) self.drawLink() def relationEditableChanged(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.setEnabled(self.relation.isValid() and self.relation.referencingLayer().isEditable()) def layerValueChangedOutside(self, fid, fieldIdx, value): if not self.relation.isValid() or not self.referencingFeature.isValid() or self.relationWidgetWrapper is None: return # not the correct feature if fid != self.referencingFeature.id(): return # not the correct field if fieldIdx != self.referencingFieldIndex(): return # widget already has this value if value == self.relationWidgetWrapper.value(): return self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged) self.relationWidgetWrapper.setValue(value) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) def referencingFieldIndex(self): if not self.relation.isValid(): return -1 fieldName = self.relation.fieldPairs().keys()[0] fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName) return fieldIdx @pyqtSlot(bool, name="on_drawButton_toggled") def drawLink(self): self.settings.setValue("drawEnabled", self.drawButton.isChecked()) self.linkRubber.reset() if not self.drawButton.isChecked() or not self.referencingFeature.isValid() or not self.relation.isValid(): return referencedFeature = self.relationReferenceWidget.referencedFeature() if not referencedFeature.isValid(): return p1 = self.centroid(self.relation.referencedLayer(), referencedFeature) p2 = self.centroid(self.relation.referencingLayer(), self.referencingFeature) geom = arc(p1, p2) self.linkRubber.setToGeometry(geom, None) self.linkRubber.setWidth(self.settings.value("rubberWidth")) self.linkRubber.setColor(self.settings.value("rubberColor")) self.linkRubber.setLineStyle(Qt.DashLine) def centroid(self, layer, feature): geom = feature.geometry() if geom.type() == QGis.Line: geom = geom.interpolate(geom.length()/2) else: geom = geom.centroid() return self.iface.mapCanvas().mapSettings().layerToMapCoordinates(layer, geom.asPoint()) @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked") def highlightReferencingFeature(self): self.deleteHighlight() if not self.relation.isValid() or not self.referencingFeature.isValid(): return self.featureHighlight = QgsHighlight(self.iface.mapCanvas(), self.referencingFeature.geometry(), self.relation.referencingLayer()) settings = QSettings() color = QColor( settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha())) bbuffer = float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM)) minWidth = float(settings.value("/Map/highlight/minWidth", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.featureHighlight.setColor(color) color.setAlpha(alpha) self.featureHighlight.setFillColor(color) self.featureHighlight.setBuffer(bbuffer) self.featureHighlight.setMinWidth(minWidth) self.featureHighlight.show() timer = QTimer(self) timer.setSingleShot(True) timer.timeout.connect(self.deleteHighlight) timer.start(3000) def deleteHighlight(self): if self.featureHighlight: del self.featureHighlight self.featureHighlight = None
class SubProfileTool(QgsMapTool): """ Tool class for making a line elevation profile """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/profile_2_icon.png' self.text = QCoreApplication.translate("VDLTools", "Line for MNT profile") self.setCursor(Qt.ArrowCursor) self.__isSelected = False self.__dockWdg = None self.__rubberLine = None self.__rubberDots = None self.ownSettings = None self.__line = None self.__startVertex = None self.__isfloating = False self.__dockGeom = None def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__dockWdg = ProfileDockWidget(self.__iface, self.__dockGeom) if self.__isfloating: self.__dockWdg.show() else: self.__iface.addDockWidget(Qt.BottomDockWidgetArea, self.__dockWdg) self.__dockWdg.closeSignal.connect(self.__closed) self.__rubberLine = QgsRubberBand(self.canvas(), QGis.Line) color = QColor("red") color.setAlphaF(0.78) self.__rubberLine.setColor(color) self.__rubberDots = QgsRubberBand(self.canvas(), QGis.Line) color = QColor("red") color.setAlphaF(0.78) self.__rubberDots.setColor(color) self.__rubberDots.setLineStyle(Qt.DotLine) def __closed(self): """ When the dock is closed """ self.__dockGeom = self.__dockWdg.geometry() self.__isfloating = self.__dockWdg.isFloating() self.__cancel() self.__iface.actionPan().trigger() def deactivate(self): """ When the action is deselected """ self.canvas().scene().removeItem(self.__rubberLine) self.__rubberLine = None if self.__dockWdg is not None: self.__dockWdg.close() QgsMapTool.deactivate(self) def __cancel(self): """ To cancel used variables """ self.__isSelected = False self.__rubberDots = None self.__line = None self.__startVertex = None def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if self.__isSelected: dots = QgsLineStringV2() dots.addVertex(self.__startVertex) dots.addVertex(QgsPointV2(event.mapPoint())) self.__rubberDots.reset() self.__rubberDots.setToGeometry(QgsGeometry(dots.clone()), None) def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if event.button() == Qt.RightButton: self.__isSelected = False self.__rubberDots.reset() self.__calculateProfile() elif event.button() == Qt.LeftButton: if not self.__isSelected: self.__isSelected = True self.__dockWdg.clearData() self.__line = QgsLineStringV2() self.__rubberLine.reset() self.__startVertex = QgsPointV2(event.mapPoint()) self.__line.addVertex(self.__startVertex) if self.__isSelected: self.__rubberLine.reset() self.__rubberLine.setToGeometry(QgsGeometry(self.__line.clone()), None) def __calculateProfile(self): """ To calculate the profile and display it """ if self.__line is None: return self.__dockWdg.clearData() if self.__line.numPoints() == 0: return points = [] for i in range(self.__line.numPoints()): points.append({'x': self.__line.pointN(i).x(), 'y': self.__line.pointN(i).y()}) self.__dockWdg.setProfiles(points, 0) self.__dockWdg.attachCurves(None, self.ownSettings, [1, 1, 1])
class PointTool(QgsMapToolEdit): ''' Implementation of interactions of the user with the main map. Will called every time the user clicks on the map or hovers the mouse over the map. ''' def deactivate(self): QgsMapTool.deactivate(self) self.deactivated.emit() def __init__(self, canvas, iface, turn_off_snap, smooth=False): ''' canvas - link to the QgsCanvas of the application iface - link to the Qgis Interface turn_off_snap - flag sets snapping to the nearest color smooth - flag sets smoothing of the traced path ''' self.iface = iface # list of Anchors for current line self.anchors = [] # for keeping track of mouse event for rubber band updating self.last_mouse_event_pos = None self.tracing_mode = TracingModes.PATH self.turn_off_snap = turn_off_snap self.smooth_line = smooth # possible variants: gray_diff, as_is, color_diff (using v from hsv) self.grid_conversion = "gray_diff" # QApplication.restoreOverrideCursor() # QApplication.setOverrideCursor(Qt.CrossCursor) QgsMapToolEmitPoint.__init__(self, canvas) self.rlayer = None self.grid_changed = None self.snap_tolerance = None self.vlayer = None self.grid = None self.sample = None self.tracking_is_active = False # False = not a polygon self.rubber_band = QgsRubberBand(self.canvas(), False) self.markers = [] self.marker_snap = QgsVertexMarker(self.canvas()) self.marker_snap.setColor(QColor(255, 0, 255)) self.find_path_task = None self.change_state(WaitingFirstPointState) def display_message( self, title, message, level='Info', duration=2, ): ''' Shows message bar to the user. `level` receives one of four possible string values: Info, Warning, Critical, Success ''' LEVELS = { 'Info': Qgis.Info, 'Warning': Qgis.Warning, 'Critical': Qgis.Critical, 'Success': Qgis.Success, } self.iface.messageBar().pushMessage(title, message, LEVELS[level], duration) def change_state(self, state): self.state = state(self) def snap_tolerance_changed(self, snap_tolerance): self.snap_tolerance = snap_tolerance if snap_tolerance is None: self.marker_snap.hide() else: self.marker_snap.show() def trace_color_changed(self, color): r, g, b = self.sample if color is False: self.grid_changed = None else: r0, g0, b0, t = color.getRgb() self.grid_changed = np.abs((r0 - r)**2 + (g0 - g)**2 + (b0 - b)**2) def get_current_vector_layer(self): try: vlayer = self.iface.layerTreeView().selectedLayers()[0] if isinstance(vlayer, QgsVectorLayer): if vlayer.wkbType() == QgsWkbTypes.MultiLineString: return vlayer else: self.display_message( " ", "The active layer must be" + " a MultiLineString vector layer", level='Warning', duration=2, ) return None else: self.display_message( "Missing Layer", "Please select vector layer to draw", level='Warning', duration=2, ) return None except IndexError: self.display_message( "Missing Layer", "Please select vector layer to draw", level='Warning', duration=2, ) return None def raster_layer_has_changed(self, raster_layer): self.rlayer = raster_layer if self.rlayer is None: self.display_message( "Missing Layer", "Please select raster layer to trace", level='Warning', duration=2, ) return try: sample, to_indexes, to_coords, to_coords_provider, \ to_coords_provider2 = \ get_whole_raster(self.rlayer, QgsProject.instance(), ) except PossiblyIndexedImageError: self.display_message( "Missing Layer", "Can't trace indexed or gray image", level='Critical', duration=2, ) return r = sample[0].astype(float) g = sample[1].astype(float) b = sample[2].astype(float) where_are_NaNs = np.isnan(r) r[where_are_NaNs] = 0 where_are_NaNs = np.isnan(g) g[where_are_NaNs] = 0 where_are_NaNs = np.isnan(b) b[where_are_NaNs] = 0 self.sample = (r, g, b) self.grid = r + g + b self.to_indexes = to_indexes self.to_coords = to_coords self.to_coords_provider = to_coords_provider self.to_coords_provider2 = to_coords_provider2 def remove_last_anchor_point(self, undo_edit=True, redraw=True): ''' Removes last anchor point and last marker point ''' # check if we have at least one feature to delete vlayer = self.get_current_vector_layer() if vlayer is None: return if vlayer.featureCount() < 1: return # remove last marker if self.markers: last_marker = self.markers.pop() self.canvas().scene().removeItem(last_marker) # remove last anchor if self.anchors: self.anchors.pop() if undo_edit: # it's a very ugly way of triggering single undo event self.iface.editMenu().actions()[0].trigger() if redraw: self.update_rubber_band() self.redraw() def keyPressEvent(self, e): if e.key() == Qt.Key_Backspace or e.key() == Qt.Key_B: # delete last segment if backspace is pressed self.remove_last_anchor_point() elif e.key() == Qt.Key_A: # change tracing mode self.tracing_mode = self.tracing_mode.next() self.update_rubber_band() elif e.key() == Qt.Key_S: # toggle snap mode self.turn_off_snap() elif e.key() == Qt.Key_Escape: # Abort tracing process self.abort_tracing_process() def add_anchor_points(self, x1, y1, i1, j1): ''' Adds anchor points and markers to self. ''' anchor = Anchor(x1, y1, i1, j1) self.anchors.append(anchor) marker = QgsVertexMarker(self.canvas()) marker.setCenter(QgsPointXY(x1, y1)) self.markers.append(marker) def trace_over_image(self, start, goal, do_it_as_task=False, vlayer=None): ''' performs tracing ''' i0, j0 = start i1, j1 = goal r, g, b, = self.sample try: r0 = r[i1, j1] g0 = g[i1, j1] b0 = b[i1, j1] except IndexError: raise OutsideMapError if self.grid_changed is None: grid = np.abs((r0 - r)**2 + (g0 - g)**2 + (b0 - b)**2) else: grid = self.grid_changed if do_it_as_task: # dirty hack to avoid QGIS crashing self.find_path_task = FindPathTask( grid.astype(np.dtype('l')), start, goal, self.draw_path, vlayer, ) QgsApplication.taskManager().addTask(self.find_path_task, ) self.tracking_is_active = True else: path, cost = FindPathFunction( grid.astype(np.dtype('l')), (i0, j0), (i1, j1), ) return path, cost def trace(self, x1, y1, i1, j1, vlayer): ''' Traces path from last point to given point. In case tracing is inactive just creates straight line. ''' if self.tracing_mode.is_tracing(): if self.snap_tolerance is not None: try: i1, j1 = self.snap(i1, j1) except OutsideMapError: return _, _, i0, j0 = self.anchors[-2] start_point = i0, j0 end_point = i1, j1 try: self.trace_over_image(start_point, end_point, do_it_as_task=True, vlayer=vlayer) except OutsideMapError: pass else: self.draw_path( None, vlayer, was_tracing=False, x1=x1, y1=y1, ) def snap(self, i, j): if self.snap_tolerance is None: return i, j if not self.tracing_mode.is_tracing(): return i, j if self.grid_changed is None: return i, j size_i, size_j = self.grid.shape size = self.snap_tolerance if i < size or j < size or i + size > size_i or j + size > size_j: raise OutsideMapError grid_small = self.grid_changed grid_small = grid_small[i - size:i + size, j - size:j + size] smallest_cells = np.where(grid_small == np.amin(grid_small)) coordinates = list(zip(smallest_cells[0], smallest_cells[1])) if len(coordinates) == 1: delta_i, delta_j = coordinates[0] delta_i -= size delta_j -= size else: # find the closest to the center deltas = [(i - size, j - size) for i, j in coordinates] lengths = [(i**2 + j**2) for i, j in deltas] i = lengths.index(min(lengths)) delta_i, delta_j = deltas[i] return i + delta_i, j + delta_j def canvasReleaseEvent(self, mouseEvent): ''' Method where the actual tracing is performed after the user clicked on the map ''' vlayer = self.get_current_vector_layer() if vlayer is None: return if not vlayer.isEditable(): self.display_message( "Edit mode", "Please begin editing vector layer to trace", level='Warning', duration=2, ) return if self.rlayer is None: self.display_message( "Missing Layer", "Please select raster layer to trace", level='Warning', duration=2, ) return if mouseEvent.button() == Qt.RightButton: self.state.click_rmb(mouseEvent, vlayer) elif mouseEvent.button() == Qt.LeftButton: self.state.click_lmb(mouseEvent, vlayer) return def draw_path(self, path, vlayer, was_tracing=True,\ x1=None, y1=None): ''' Draws a path after tracer found it. ''' if was_tracing: if self.smooth_line: path = smooth(path, size=5) path = simplify(path) current_last_point = self.to_coords(*path[-1]) path_ref = [self.to_coords_provider(i, j) for i, j in path] else: x0, y0, _, _ = self.anchors[-2] path_ref = [ self.to_coords_provider2(x0, y0), self.to_coords_provider2(x1, y1) ] current_last_point = (x1, y1) self.ready = False if len(self.anchors) == 2: vlayer.beginEditCommand("Adding new line") add_feature_to_vlayer(vlayer, path_ref) vlayer.endEditCommand() else: x0, y0, _, _ = self.anchors[-2] last_point = self.to_coords_provider2(x0, y0) path_ref = [last_point] + path_ref[1:] vlayer.beginEditCommand("Adding new segment to the line") add_to_last_feature(vlayer, path_ref) vlayer.endEditCommand() _, _, current_last_point_i, current_last_point_j = self.anchors[-1] self.anchors[-1] = current_last_point[0], current_last_point[ 1], current_last_point_i, current_last_point_j self.redraw() self.tracking_is_active = False def update_rubber_band(self): # this is very ugly but I can't make another way if self.last_mouse_event_pos is None: return if not self.anchors: return x0, y0, _, _ = self.anchors[-1] qgsPoint = self.toMapCoordinates(self.last_mouse_event_pos) x1, y1 = qgsPoint.x(), qgsPoint.y() points = [QgsPoint(x0, y0), QgsPoint(x1, y1)] self.rubber_band.setColor(QColor(255, 0, 0)) self.rubber_band.setWidth(3) self.rubber_band.setLineStyle( RUBBERBAND_LINE_STYLES[self.tracing_mode], ) vlayer = self.get_current_vector_layer() if vlayer is None: return self.rubber_band.setToGeometry( QgsGeometry.fromPolyline(points), self.vlayer, ) def canvasMoveEvent(self, mouseEvent): ''' Store the mouse position for the correct updating of the rubber band ''' # we need at least one point to draw if not self.anchors: return if self.snap_tolerance is not None and self.tracing_mode.is_tracing(): qgsPoint = self.toMapCoordinates(mouseEvent.pos()) x1, y1 = qgsPoint.x(), qgsPoint.y() # i, j = get_indxs_from_raster_coords(self.geo_ref, x1, y1) i, j = self.to_indexes(x1, y1) try: i1, j1 = self.snap(i, j) except OutsideMapError: return # x1, y1 = get_coords_from_raster_indxs(self.geo_ref, i1, j1) x1, y1 = self.to_coords(i1, j1) self.marker_snap.setCenter(QgsPointXY(x1, y1)) self.last_mouse_event_pos = mouseEvent.pos() self.update_rubber_band() self.redraw() def abort_tracing_process(self): ''' Terminate background process of tracing raster after the user hits Esc. ''' # check if we have any tasks if self.find_path_task is None: return self.tracking_is_active = False try: # send terminate signal to the task self.find_path_task.cancel() self.find_path_task = None except RuntimeError: return else: self.remove_last_anchor_point(undo_edit=False, ) def redraw(self): # If caching is enabled, a simple canvas refresh might not be # sufficient to trigger a redraw and you must clear the cached image # for the layer if self.iface.mapCanvas().isCachingEnabled(): vlayer = self.get_current_vector_layer() if vlayer is None: return vlayer.triggerRepaint() self.iface.mapCanvas().refresh() QgsApplication.processEvents() def pan(self, x, y): ''' Move the canvas to the x, y position ''' currExt = self.iface.mapCanvas().extent() canvasCenter = currExt.center() dx = x - canvasCenter.x() dy = y - canvasCenter.y() xMin = currExt.xMinimum() + dx xMax = currExt.xMaximum() + dx yMin = currExt.yMinimum() + dy yMax = currExt.yMaximum() + dy newRect = QgsRectangle(xMin, yMin, xMax, yMax) self.iface.mapCanvas().setExtent(newRect)
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 mapillary_cursor(): def transformToWGS84(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs( ) # get current crs crsSrc = crcMappaCorrente crsDest = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def transformToCurrentSRS(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs( ) # get current crs crsDest = crcMappaCorrente crsSrc = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def __init__(self, parentInstance): self.parentInstance = parentInstance self.iface = parentInstance.iface self.mapCanvas = self.iface.mapCanvas() self.lineOfSight = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.sightDirection = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.pointOfView = QgsVertexMarker(self.mapCanvas) self.cursor = QgsVertexMarker(self.mapCanvas) self.sightDirection.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setColor(QColor(CURSOR_COLOR)) self.pointOfView.setColor(QColor(CURSOR_COLOR)) self.cursor.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setWidth(2) self.sightDirection.setWidth(1) self.sightDirection.setLineStyle(Qt.DashLine) self.pointOfView.setIconType(QgsRubberBand.ICON_CIRCLE) self.cursor.setIconType(QgsRubberBand.ICON_CIRCLE) self.pointOfView.setIconSize(20) self.cursor.setIconSize(20) self.cursor.setPenWidth(2) self.pointOfView.setPenWidth(2) self.samples_datasource = '' self.delete() #self.update_ds(self.parentInstance.sample_settings.settings['sample_source']) def getSamplesLayer(self, samples_datasource): if samples_datasource != 'memory': if not os.path.exists(samples_datasource): self.create_datasource_from_template(samples_datasource) samples_lyr = QgsVectorLayer(samples_datasource, SAMPLES_LAYER_NAME, 'ogr') else: samples_lyr = QgsVectorLayer("Point?crs=epsg:4326&index=yes", SAMPLES_LAYER_NAME, 'memory') self.checkForTemplateFields(samples_lyr) return samples_lyr def getFieldFromDefinition(self, field_type): type_pack = field_type[1].split("|") if field_type[2]: comment = field_type[2] else: comment = field_type[0] return QgsField(name=field_type[0], type=int(type_pack[0]), len=int(type_pack[1]), prec=int(type_pack[2]), comment=comment) def checkForTemplateFields(self, layer): layerFieldNamesList = [] for field in layer.fields().toList(): layerFieldNamesList.append(field.name()) for fieldDef in FIELDS_TEMPLATE: if not fieldDef[0] in layerFieldNamesList: layer.startEditing() layer.addAttribute(self.getFieldFromDefinition(fieldDef)) layer.commitChanges() def update_ds(self, ds): if self.samples_datasource != ds: self.samples_datasource = ds self.samplesLayer = self.getSamplesLayer(ds) self.samplesLayer.triggerRepaint() self.samplesLayer.loadNamedStyle( os.path.join(os.path.dirname(__file__), "res", "mapillary_samples.qml")) self.samplesLayer.featureAdded.connect(self.newAddedFeat) def create_datasource_from_template(self, datasource): fieldSet = QgsFields() for fieldDef in FIELDS_TEMPLATE: fieldSet.append(self.getFieldFromDefinition(fieldDef)) writer = QgsVectorFileWriter(datasource, 'UTF-8', fieldSet, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326), "ESRI Shapefile") if writer.hasError(): print("error", writer.errorMessage()) del writer def draw(self, pointOfView_coords, orig_pointOfView_coords, cursor_coords, endOfSight_coords): if pointOfView_coords[0]: self.cursor.show() self.pointOfView.show() self.lineOfSight.reset() self.sightDirection.reset() pointOfView = self.transformToCurrentSRS( QgsPointXY(pointOfView_coords[1], pointOfView_coords[0])) cursor = self.transformToCurrentSRS( QgsPointXY(cursor_coords[1], cursor_coords[0])) endOfSight = self.transformToCurrentSRS( QgsPointXY(endOfSight_coords[1], endOfSight_coords[0])) self.pointOfView.setCenter(pointOfView) self.cursor.setCenter(cursor) self.lineOfSight.addPoint(pointOfView) self.lineOfSight.addPoint(cursor) self.sightDirection.addPoint(pointOfView) self.sightDirection.addPoint(endOfSight) self.cursor.updatePosition() else: self.delete() def delete(self): self.cursor.hide() self.pointOfView.hide() self.lineOfSight.reset() self.sightDirection.reset() def addSampleLayerToCanvas(self): if not QgsProject.instance().mapLayer(self.samplesLayer.id()): QgsProject.instance().addMapLayer(self.samplesLayer) self.restoreMarkers() def sample(self, type, id, key, sample_coords, img_coords=None): self.samplesLayer.startEditing() samplePoint = QgsPointXY(sample_coords[1], sample_coords[0]) #sampleDevicePoint = self.iface.mapCanvas().getCoordinateTransform().transform(samplePoint.x(),samplePoint.y()) self.addSampleLayerToCanvas() sampleFeat = QgsFeature(self.samplesLayer.fields()) sampleFeat['type'] = type sampleFeat['id'] = id sampleFeat['key'] = key if img_coords: sampleFeat['img_coords'] = img_coords sampleFeat.setGeometry(QgsGeometry.fromPointXY(samplePoint)) #self.samplesLayer.dataProvider().addFeatures([sampleFeat]) print('', self.samplesLayer.addFeature(sampleFeat)) self.samplesLayer.commitChanges() def newAddedFeat(self, featId): if featId < 0: return self.samplesLayer.triggerRepaint() if self.parentInstance.sample_settings.settings['auto_open_form']: newFeat = self.samplesLayer.getFeature(featId) self.parentInstance.samples_form.open(newFeat) def getSamplesList(self): samples = [] id = 1 for feat in self.samplesLayer.getFeatures(): samples.append({ "id": id, "latLon": { 'lat': feat.geometry().asPoint().y(), 'lon': feat.geometry().asPoint().x(), } }) def restoreTags(self, key): exp = QgsExpression('"type" = \'tag\' and "key" = \'%s\'' % key) tags = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings[ 'categories'][str(feat['cat'])] else: color = '#ffffff' tags.append({ 'id': str(feat['id']), 'key': str(feat['key']), 'note': str(feat['note']), 'cat': str(feat['cat']), 'color': color, 'geometry': json.loads(feat['img_coords']) }) return tags def editSample(self, type, key, id): exp = QgsExpression( '"type" = \'%s\' and "key" = \'%s\' and "id" = \'%s\'' % (type, key, id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): self.parentInstance.samples_form.open(feat) def moveMarker(self, key, id, sample_coords): exp = QgsExpression( '"type" = \'marker\' and "key" = \'%s\' and "id" = \'%s\'' % (key, id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): samplePoint = QgsPointXY(sample_coords[1], sample_coords[0]) self.samplesLayer.startEditing() self.samplesLayer.changeGeometry( feat.id(), QgsGeometry.fromPointXY(samplePoint)) self.samplesLayer.commitChanges() self.samplesLayer.triggerRepaint() def restoreMarkers(self): if self.parentInstance.sample_settings.settings[ 'sample_source'] != 'memory': exp = QgsExpression('"type" = \'marker\'') markersDef = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings[ 'categories'][str(feat['cat'])] else: color = '#ffffff' markersDef.append({ 'key': str(feat['key']), 'id': str(feat['id']), 'color': color, 'loc': { 'lon': feat.geometry().asPoint().x(), 'lat': feat.geometry().asPoint().y() } }) self.parentInstance.viewer.addMarkers(markersDef)
class VoGISProfilToolMainDialog(QDialog): def __init__(self, interface, settings): QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolMain() self.ui.setupUi(self) self.ui.buttonBox.button(QDialogButtonBox.Ok).setText(QApplication.translate("code", "Profil erstellen")) self.ui.buttonBox.button(QDialogButtonBox.Cancel).setText(QApplication.translate("code", "Schließen")) self.ui.grpCadastre.toggled.connect(self._toggleCadastreLayer) self.ui.cmbCadastreLayer.currentIndexChanged.connect(self._updateCadastreLayer) self.settings = settings self.iface = interface self.selectingVisibleRasters = False self.thread = None if self.settings.onlyHektoMode is True: self.ui.IDC_widRaster.hide() self.adjustSize() self.ui.IDC_dblspinDistance.setValue(self.settings.equiDistance) self.ui.IDC_dblspinVertexCnt.setValue(self.settings.vertexCnt) validator = QIntValidator(-32768, 32768, self) self.ui.IDC_tbNoDataExport.setValidator(validator) self.ui.IDC_tbFromX.setText("-30000") self.ui.IDC_tbFromY.setText("240000") self.ui.IDC_tbToX.setText("-20000") self.ui.IDC_tbToY.setText("230000") self.__addRastersToGui() self.__addPolygonsToGui() for line_lyr in self.settings.mapData.lines.lines(): self.ui.IDC_cbLineLayers.addItem(line_lyr.name, line_lyr) if self.settings.mapData.lines.count() < 1: self.ui.IDC_rbDigi.setChecked(True) self.ui.IDC_rbShapeLine.setEnabled(False) #Einstellungen fuer Linie zeichen self.action = QAction( QIcon(":/plugins/vogisprofiltoolmain/icons/icon.png"), "VoGIS-Profiltool", self.iface.mainWindow()) self.action.setWhatsThis("VoGIS-Profiltool") self.canvas = self.iface.mapCanvas() self.tool = ProfiletoolMapTool(self.canvas, self.action) self.savedTool = self.canvas.mapTool() self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) self.rubberband.setLineStyle(Qt.SolidLine) self.rubberband.setWidth(4.0) self.rubberband.setColor(QColor(0, 255, 0)) #http://www.qgis.org/api/classQgsRubberBand.html#a6f7cdabfcf69b65dfc6c164ce2d01fab self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def accept(self): try: nodata = self.ui.IDC_tbNoDataExport.text() self.settings.nodata_value = int(nodata) if nodata != "" else None QgsMessageLog.logMessage("Maindlg: nodata: {0}".format(self.settings.nodata_value), "VoGis", Qgis.Info) if self.settings.onlyHektoMode is True and self.settings.mapData.rasters.count() > 0: self.settings.onlyHektoMode = False if self.settings.onlyHektoMode is False: if self.settings.mapData.rasters.count() < 1: retVal = QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate("code", "Keine Rasterebene vorhanden oder sichtbar! Nur hektometrieren?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if retVal == QMessageBox.No: return else: self.settings.onlyHektoMode = True self.settings.createHekto = True if self.__getSettingsFromGui() is False: return if self.settings.onlyHektoMode is False: if len(self.settings.mapData.rasters.selectedRasters()) < 1: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate("code", "Kein Raster selektiert!")) return QgsMessageLog.logMessage("modeLine!=line: {0}".format(self.settings.modeLine != enumModeLine.line), "VoGis", Qgis.Info) QgsMessageLog.logMessage("customLine is None: {0}".format(self.settings.mapData.customLine is None), "VoGis", Qgis.Info) if self.settings.modeLine != enumModeLine.line and self.settings.mapData.customLine is None: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate("code", "Keine Profillinie vorhanden!")) return if len(self.settings.mapData.polygons.selected_polygons()) > 0 and len(self.settings.mapData.rasters.selectedRasters()) > 1: raster_names = list(raster.name for raster in self.settings.mapData.rasters.selectedRasters()) sel_raster, ok_clicked = QInputDialog.getItem( self.iface.mainWindow(), "DHM?", "Welches DHM soll zur Flächenverschneidung verwendet werden?", raster_names, 0, False ) if ok_clicked is False: return self.settings.intersection_dhm_idx = raster_names.index(sel_raster) QApplication.setOverrideCursor(Qt.WaitCursor) create_profile = CreateProfile(self.iface, self.settings) thread = QThread(self) create_profile.moveToThread(thread) create_profile.finished.connect(self.profiles_finished) create_profile.error.connect(self.profiles_error) create_profile.progress.connect(self.profiles_progress) thread.started.connect(create_profile.create) thread.start(QThread.LowestPriority) self.thread = thread self.create_profile = create_profile self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) except: QApplication.restoreOverrideCursor() ex = "{0}".format(traceback.format_exc()) msg = "Unexpected ERROR:\n\n{0}".format(ex[:2000]) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", msg) def profiles_finished(self, profiles, intersections, cadastre): QApplication.restoreOverrideCursor() self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) self.thread.quit() self.thread.wait() #QGIS 2.0 http://gis.stackexchange.com/a/58754 http://gis.stackexchange.com/a/57090 self.iface.mainWindow().statusBar().showMessage("VoGIS-Profiltool, {0} Profile".format(len(profiles))) QgsMessageLog.logMessage("Profile Count: {0}".format(len(profiles)), "VoGis", Qgis.Info) if len(profiles) < 1: QApplication.restoreOverrideCursor() QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate("code", "Es konnten keine Profile erstellt werden.")) return dlg = VoGISProfilToolPlotDialog(self.iface, self.settings, profiles, intersections, cadastre) dlg.show() dlg.exec_() def profiles_error(self, exception_string): QApplication.restoreOverrideCursor() QgsMessageLog.logMessage("Error during profile creation: {0}".format(exception_string), "VoGis", Qgis.Critical) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", exception_string) def profiles_progress(self, msg): self.iface.mainWindow().statusBar().showMessage(msg) self.ui.IDC_lblCreateStatus.setText(msg) QApplication.processEvents() def reject(self): if not self.thread is None: if self.thread.isRunning(): self.create_profile.abort() return self.rubberband.reset(self.polygon) QDialog.reject(self) def selectVisibleRasters(self): self.refreshRasterList() self.selectingVisibleRasters = True extCanvas = self.iface.mapCanvas().extent() #alle raster in den einstellunge deselektieren for r in self.settings.mapData.rasters.rasters(): r.selected = False #alle raster in der ListView deselektieren for idx in range(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) item.setCheckState(Qt.Unchecked) #Raster im Extent selektieren canvasCrs = self.iface.mapCanvas().mapSettings().destinationCrs() for idx in range(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) raster = item.data(Qt.UserRole) for r in self.settings.mapData.rasters.rasters(): layerCrs = r.grid.crs() ct = QgsCoordinateTransform(layerCrs, canvasCrs, QgsProject.instance()) extent = ct.transform(r.grid.extent()) if extCanvas.intersects(extent): if r.id == raster.id: r.selected = True item.setCheckState(Qt.Checked) self.selectingVisibleRasters = False def lineLayerChanged(self, idx): if self.ui.IDC_rbShapeLine.isChecked() is False: self.ui.IDC_rbShapeLine.setChecked(True) lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) lyr = lineLyr.line if hasattr(lyr, "selectedFeatureCount"): if(lyr.selectedFeatureCount() < 1): self.ui.IDC_chkOnlySelectedFeatures.setChecked(False) else: self.ui.IDC_chkOnlySelectedFeatures.setChecked(True) def valueChangedEquiDistance(self, val): if self.ui.IDC_rbEquiDistance.isChecked() is False: self.ui.IDC_rbEquiDistance.setChecked(True) def valueChangedVertexCount(self, val): if self.ui.IDC_rbVertexCount.isChecked() is False: self.ui.IDC_rbVertexCount.setChecked(True) def lvRasterItemChanged(self, item): if self.selectingVisibleRasters is True: return if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) raster_lyr = item_data self.settings.mapData.rasters.getById(raster_lyr.id).selected = selected def lvPolygonItemChanged(self, item): if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) poly_lyr = item_data self.settings.mapData.polygons.getById(poly_lyr.id).selected = selected def refreshRasterList(self): root = QgsProject.instance().layerTreeRoot() avail_lyrs = root.findLayers() raster_coll = RasterCollection() for lyr in avail_lyrs: if lyr.isVisible(): mapLayer = lyr.layer() lyr_type = mapLayer.type() lyr_name = mapLayer.name() if lyr_type == QgsMapLayer.RasterLayer: if mapLayer.bandCount() < 2: new_raster = Raster(mapLayer.id(), lyr_name, mapLayer) raster_coll.addRaster(new_raster) self.settings.mapData.rasters = raster_coll self.__addRastersToGui() def __addRastersToGui(self): self.ui.IDC_listRasters.clear() check = Qt.Unchecked if self.settings.mapData.rasters.count() == 1: check = Qt.Checked self.settings.mapData.rasters.rasters()[0].selected = True for raster_lyr in self.settings.mapData.rasters.rasters(): item = QListWidgetItem(raster_lyr.name) item.setData(Qt.UserRole, raster_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listRasters.addItem(item) def __addPolygonsToGui(self): self.ui.IDC_listPolygons.clear() self.ui.cmbCadastreLayer.clear() check = Qt.Unchecked for poly_lyr in self.settings.mapData.polygons.polygons(): item = QListWidgetItem(poly_lyr.name) item.setData(Qt.UserRole, poly_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listPolygons.addItem(item) self.ui.cmbCadastreLayer.addItem(poly_lyr.name, poly_lyr.id) def drawLine(self): if self.ui.IDC_rbDigi.isChecked() is False: self.ui.IDC_rbDigi.setChecked(True) self.dblckltemp = None self.rubberband.reset(self.polygon) self.__cleanDigi() self.__activateDigiTool() self.canvas.setMapTool(self.tool) def __createDigiFeature(self, pnts): u = Util(self.iface) f = u.createQgLineFeature(pnts) self.settings.mapData.customLine = f def __lineFinished(self, position): self.showNormal() self.raise_() self.activateWindow() mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPointXY(mapPos.x(), mapPos.y()) self.pointsToDraw.append(newPoint) #launch analyses self.iface.mainWindow().statusBar().showMessage(str(self.pointsToDraw)) if len(self.pointsToDraw) < 2: self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint self.drawnLine = None QMessageBox.warning(self, "VoGIS-Profiltool", QApplication.translate("code", "Profillinie digitalisieren abgebrochen!")) self.drawnLine = self.__createDigiFeature(self.pointsToDraw) self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint def __cleanDigi(self): self.pointsToDraw = [] self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.savedTool) def __activateDigiTool(self): self.tool.moved.connect(self.__moved) self.tool.rightClicked.connect(self.__rightClicked) self.tool.leftClicked.connect(self.__leftClicked) self.tool.doubleClicked.connect(self.__doubleClicked) self.tool.deactivated.connect(self.__deactivateDigiTool) def __deactivateDigiTool(self): # TODO: how to check if not connected??? try: self.tool.moved.disconnect(self.__moved) except: pass try: self.tool.leftClicked.disconnect(self.__leftClicked) except: pass try: self.tool.rightClicked.disconnect(self.__rightClicked) except: pass try: self.tool.doubleClicked.disconnect(self.__doubleClicked) except: pass try: self.iface.mainWindow().statusBar().showMessage("") except: pass def __moved(self, position): if len(self.pointsToDraw) > 0: mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) self.rubberband.reset(self.polygon) newPnt = QgsPointXY(mapPos.x(), mapPos.y()) pnts = self.pointsToDraw + [newPnt] self.rubberband.setToGeometry(QgsGeometry.fromPolylineXY(pnts), None) def __rightClicked(self, position): self.__lineFinished(position) def __leftClicked(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPointXY(mapPos.x(), mapPos.y()) if newPoint == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointsToDraw) == 0: self.rubberband.reset(self.polygon) self.pointsToDraw.append(newPoint) def __doubleClicked(self, position): pass #not in use right now def __lineCancel(self): pass def __getSettingsFromGui(self): self.settings.linesExplode = (self.ui.IDC_chkLinesExplode.checkState() == Qt.Checked) self.settings.linesMerge = (self.ui.IDC_chkLinesMerge.checkState() == Qt.Checked) self.settings.onlySelectedFeatures = (self.ui.IDC_chkOnlySelectedFeatures.checkState() == Qt.Checked) self.settings.equiDistance = self.ui.IDC_dblspinDistance.value() self.settings.vertexCnt = self.ui.IDC_dblspinVertexCnt.value() #self.settings.createHekto = (self.ui.IDC_chkCreateHekto.checkState() == Qt.Checked) self.settings.nodesAndVertices = (self.ui.IDC_chkNodesAndVertices.checkState() == Qt.Checked) self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) if self.settings.onlySelectedFeatures is True and self.settings.mapData.selectedLineLyr.line.selectedFeatureCount() < 1: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate("code", u"Der gewählte Layer hat keine selektierten Elemente.")) return False if self.ui.IDC_rbDigi.isChecked(): self.settings.modeLine = enumModeLine.customLine elif self.ui.IDC_rbShapeLine.isChecked(): self.settings.modeLine = enumModeLine.line else: #self.ui.IDC_rbStraigthLine self.settings.modeLine = enumModeLine.straightLine if self.ui.IDC_rbEquiDistance.isChecked(): self.settings.modeVertices = enumModeVertices.equiDistant else: self.settings.modeVertices = enumModeVertices.vertexCnt if self.ui.IDC_rbStraigthLine.isChecked(): ut = Util(self.iface) if ut.isFloat(self.ui.IDC_tbFromX.text(), QApplication.translate("code", "Rechtswert von")) is False: return False else: fromX = float(self.ui.IDC_tbFromX.text()) if ut.isFloat(self.ui.IDC_tbFromY.text(), QApplication.translate("code", "Hochwert von")) is False: return False else: fromY = float(self.ui.IDC_tbFromY.text()) if ut.isFloat(self.ui.IDC_tbToX.text(), QApplication.translate("code", "Rechtswert nach")) is False: return False else: toX = float(self.ui.IDC_tbToX.text()) if ut.isFloat(self.ui.IDC_tbToY.text(), QApplication.translate("code", "Hochwert nach")) is False: return False else: toY = float(self.ui.IDC_tbToY.text()) fromPnt = QgsPointXY(fromX, fromY) toPnt = QgsPointXY(toX, toY) self.settings.mapData.customLine = ut.createQgLineFeature([fromPnt, toPnt]) return True def _toggleCadastreLayer(self, toggled): if toggled: layerId = self.ui.cmbCadastreLayer.itemData(self.ui.cmbCadastreLayer.currentIndex()) self.settings.mapData.cadastre = layerId else: self.settings.mapData.cadastre = None def _updateCadastreLayer(self, index): if self.ui.grpCadastre.isChecked(): layerId = self.ui.cmbCadastreLayer.itemData(index) self.settings.mapData.cadastre = layerId
class QGISRedCreatePipeTool(QgsMapTool): def __init__(self, button, iface, projectDirectory, netwName, parent): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.parent = parent self.setAction(button) self.startMarker = QgsVertexMarker(self.iface.mapCanvas()) self.startMarker.setColor(QColor(255, 87, 51)) self.startMarker.setIconSize(15) self.startMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.startMarker.setPenWidth(3) self.startMarker.hide() self.endMarker = QgsVertexMarker(self.iface.mapCanvas()) self.endMarker.setColor(QColor(255, 87, 51)) self.endMarker.setIconSize(15) self.endMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.endMarker.setPenWidth(3) self.endMarker.hide() self.snapper = None self.rubberBand1 = None self.rubberBand2 = None self.resetProperties() def activate(self): QgsMapTool.activate(self) # 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(2) config.setUnits(2) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def deactivate(self): self.resetProperties() QgsMapTool.deactivate(self) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def resetProperties(self): # self.toolbarButton.setChecked(False) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.startMarker.hide() self.endMarker.hide() self.mousePoints = [] self.firstClicked = False self.objectSnapped = None self.rubberBand1 = None self.rubberBand2 = None def createRubberBand(self, points): myPoints1 = [] for p in points: myPoints1.append(QgsPoint(p.x(), p.y())) myPoints1.remove(myPoints1[-1]) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) self.rubberBand1 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand1.setToGeometry(QgsGeometry.fromPolyline(myPoints1), None) self.rubberBand1.setColor(QColor(240, 40, 40)) self.rubberBand1.setWidth(1) self.rubberBand1.setLineStyle(Qt.SolidLine) myPoints2 = [] myPoints2.append(QgsPoint(points[-2].x(), points[-2].y())) myPoints2.append(QgsPoint(points[-1].x(), points[-1].y())) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.rubberBand2 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand2.setToGeometry(QgsGeometry.fromPolyline(myPoints2), None) self.rubberBand2.setColor(QColor(240, 40, 40)) self.rubberBand2.setWidth(1) self.rubberBand2.setLineStyle(Qt.DashLine) """Events""" def canvasPressEvent(self, event): if event.button() == Qt.LeftButton: if not self.firstClicked: self.firstClicked = True point = self.toMapCoordinates(event.pos()) if self.objectSnapped is not None: point = self.objectSnapped.point() self.mousePoints.append(point) self.mousePoints.append(point) else: self.mousePoints.append(self.mousePoints[-1]) self.createRubberBand(self.mousePoints) if event.button() == Qt.RightButton: self.mousePoints.remove(self.mousePoints[-1]) if self.firstClicked: if (len(self.mousePoints) == 2 and self.mousePoints[0] == self.mousePoints[1]): createdPipe = False elif len(self.mousePoints) < 2: createdPipe = False else: createdPipe = True if createdPipe: self.parent.runCreatePipe(self.mousePoints) self.resetProperties() def canvasMoveEvent(self, event): # Mouse not clicked if not self.firstClicked: match = self.snapper.snapToMap(self.toMapCoordinates(event.pos())) if match.isValid(): self.objectSnapped = match self.startMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.startMarker.show() else: self.objectSnapped = None self.startMarker.hide() # Mouse clicked else: point = self.toMapCoordinates(event.pos()) match = self.snapper.snapToMap(point) if match.isValid(): self.objectSnapped = match self.endMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.endMarker.show() self.mousePoints[-1] = match.point() else: self.objectSnapped = None self.endMarker.hide() self.mousePoints[-1] = point self.createRubberBand(self.mousePoints)
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 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 ShLocatorFilter(QgsLocatorFilter): HEADERS = {b'User-Agent': b'Mozilla/5.0 QGIS ShLocator Filter'} message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget) def __init__(self, iface: QgisInterface = None): """" :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise """ super().__init__() self.iface = iface self.settings = Settings() # following properties will only be used in main thread self.rubber_band = None self.map_canvas: QgsMapCanvas = None self.transform_ch = None self.current_timer = None self.result_found = False self.nam_fetch_feature = None if iface is not None: # happens only in main thread self.map_canvas = iface.mapCanvas() self.map_canvas.destinationCrsChanged.connect( self.create_transforms) self.rubber_band = QgsRubberBand( self.map_canvas, QgsWkbTypes.PolygonGeometry) self.rubber_band.setColor(QColor(255, 50, 50, 200)) self.rubber_band.setFillColor(QColor(255, 255, 50, 160)) self.rubber_band.setBrushStyle(Qt.SolidPattern) self.rubber_band.setLineStyle(Qt.SolidLine) self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE) self.rubber_band.setIconSize(15) self.rubber_band.setWidth(4) self.rubber_band.setBrushStyle(Qt.NoBrush) self.create_transforms() def name(self): return 'ShLocator' def clone(self): return ShLocatorFilter() def priority(self): return QgsLocatorFilter.Highest def displayName(self): return 'ShLocator' def prefix(self): return 'shl' def clearPreviousResults(self): if self.rubber_band: self.rubber_band.reset(QgsWkbTypes.PointGeometry) if self.current_timer is not None: self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def hasConfigWidget(self): return True def openConfigWidget(self, parent=None): dlg = ConfigDialog(parent) dlg.exec_() def create_transforms(self): # this should happen in the main thread src_crs_ch = QgsCoordinateReferenceSystem('EPSG:2056') assert src_crs_ch.isValid() dst_crs = self.map_canvas.mapSettings().destinationCrs() self.transform_ch = QgsCoordinateTransform( src_crs_ch, dst_crs, QgsProject.instance()) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url.url() def fetchResults(self, search: str, context: QgsLocatorContext, feedback: QgsFeedback): try: dbg_info("start shlocator search...") if len(search) < 3: return self.result_found = False params = { 'query': str(search), 'maxfeatures': str(self.settings.value('max_features')) } nam = NetworkAccessManager() feedback.canceled.connect(nam.abort) url = self.url_with_param( self.settings.value('service_url'), params) dbg_info(url) try: (response, content) = nam.request( url, headers=self.HEADERS, blocking=True) self.handle_response(response, search) except RequestsExceptionUserAbort: pass except RequestsException as err: info(err, Qgis.Info) if not self.result_found: result = QgsLocatorResult() result.filter = self result.displayString = self.tr('No result found.') result.userData = NoResult self.resultFetched.emit(result) except Exception as e: info(e, Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) info(traceback.print_exception( exc_type, exc_obj, exc_traceback), Qgis.Critical) def data_product_qgsresult(self, data: dict) -> QgsLocatorResult: result = QgsLocatorResult() result.filter = self result.displayString = data['display'] result.group = 'Karten' result.userData = DataProductResult( type=data['type'], dataproduct_id=data['dataproduct_id'], display=data['display'], dset_info=data['dset_info'], sublayers=data.get('sublayers', None) ) data_product = 'dataproduct' data_type = data['type'] result.icon, result.description = dataproduct2icon_description( data_product, data_type) return result def handle_response(self, response, search_text: str): try: if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): info("Error in main response with status code: " "{} from {}".format(response.status_code, response.url)) return display_name_field = QgsField('display_name', QVariant.String) fields = QgsFields() fields.append(display_name_field) features = QgsJsonUtils.stringToFeatureList(response.content.decode('utf-8'), fields, QTextCodec.codecForName('UTF-8')) dbg_info('Found {} features'.format(len(features))) dbg_info('Data {}'.format(response.content.decode('utf-8'))) for feature in features: dbg_info('Adding feature {}'.format(feature['display_name'])) result = QgsLocatorResult() result.filter = self result.group = 'Objekte' result.displayString = feature['display_name'] result.userData = FeatureResult(feature) self.resultFetched.emit(result) self.result_found = True except Exception as e: info(str(e), Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) info(traceback.print_exception( exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result: QgsLocatorResult): # this is run in the main thread, i.e. map_canvas is not None self.clearPreviousResults() if type(result.userData) == NoResult: pass elif type(result.userData) == FeatureResult: self.highlight(result.userData.feature.geometry()) else: info('Incorrect result. Please contact support', Qgis.Critical) def highlight(self, geometry: QgsGeometry): self.rubber_band.reset(geometry.type()) self.rubber_band.addGeometry(geometry, None) rect = geometry.boundingBox() if not self.settings.value('keep_scale'): if rect.isEmpty(): current_extent = self.map_canvas.extent() rect = current_extent.scaled(self.settings.value( 'point_scale')/self.map_canvas.scale(), rect.center()) else: rect.scale(4) self.map_canvas.setExtent(rect) self.map_canvas.refresh() # self.current_timer = QTimer() # self.current_timer.timeout.connect(self.clearPreviousResults) # self.current_timer.setSingleShot(True) # self.current_timer.start(5000) def parse_feature_response(self, response): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): info("Error in feature response with status code: " "{} from {}".format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) geometry_type = data['geometry']['type'] geometry = QgsGeometry() if geometry_type == 'Point': geometry = QgsGeometry.fromPointXY(QgsPointXY(data['geometry']['coordinates'][0], data['geometry']['coordinates'][1])) elif geometry_type == 'Polygon': rings = data['geometry']['coordinates'] for r in range(0, len(rings)): for p in range(0, len(rings[r])): rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1]) geometry = QgsGeometry.fromPolygonXY(rings) elif geometry_type == 'MultiPolygon': islands = data['geometry']['coordinates'] for i in range(0, len(islands)): for r in range(0, len(islands[i])): for p in range(0, len(islands[i][r])): islands[i][r][p] = QgsPointXY( islands[i][r][p][0], islands[i][r][p][1]) geometry = QgsGeometry.fromMultiPolygonXY(islands) else: # ShLocator does not handle {geometry_type} yet. Please contact support info('ShLocator unterstützt den Geometrietyp {geometry_type} nicht.' ' Bitte kontaktieren Sie den Support.'.format(geometry_type=geometry_type), Qgis.Warning) geometry.transform(self.transform_ch) self.highlight(geometry)
class DEMto3DDialog(QtGui.QDialog, Ui_DEMto3DDialogBase): # Layer to print layer = None ''' Region of interest properties ''' map_crs = None roi_x_max = 0 roi_x_min = 0 roi_y_max = 0 roi_y_min = 0 z_max = 0 z_min = 0 ''' Model dimensions ''' height = 0 width = 0 scale = 0 z_scale = 0 ''' Raster properties ''' cell_size = 0 cols = 0 rows = 0 raster_x_max = 0 raster_x_min = 0 raster_y_max = 0 raster_y_min = 0 def __init__(self, iface): """Constructor.""" QDialog.__init__(self) self.ui = Ui_DEMto3DDialogBase() self.ui.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() try: self.map_crs = self.canvas.mapSettings().destinationCrs() except: self.map_crs = self.canvas.mapRenderer().destinationCrs() # region Layer action # fill layer combobox with raster visible layers in mapCanvas self.viewLayers = self.canvas.layers() self.ui.LayerComboBox.clear() for layer in self.viewLayers: if layer.type() == 1: self.ui.LayerComboBox.addItem(layer.name()) self.layer = get_layer(self.ui.LayerComboBox.currentText()) self.get_raster_properties() QtCore.QObject.connect(self.ui.LayerComboBox, QtCore.SIGNAL(_fromUtf8("activated(QString)")), self.get_layer) # endregion # region Extension actions self.extent = None QtCore.QObject.connect(self.ui.FullExtToolButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.full_extent) QtCore.QObject.connect(self.ui.LayerExtToolButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.layer_extent) QtCore.QObject.connect(self.ui.CustomExtToolButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.custom_extent) QtCore.QObject.connect(self.ui.XMinLineEdit, QtCore.SIGNAL(_fromUtf8("returnPressed()")), self.upload_extent) QtCore.QObject.connect(self.ui.XMaxLineEdit, QtCore.SIGNAL(_fromUtf8("returnPressed()")), self.upload_extent) QtCore.QObject.connect(self.ui.YMaxLineEdit, QtCore.SIGNAL(_fromUtf8("returnPressed()")), self.upload_extent) QtCore.QObject.connect(self.ui.YMinLineEdit, QtCore.SIGNAL(_fromUtf8("returnPressed()")), self.upload_extent) # endregion # region Dimension actions QtCore.QObject.connect(self.ui.HeightLineEdit, QtCore.SIGNAL(_fromUtf8("textEdited(QString)")), self.upload_size_from_height) QtCore.QObject.connect(self.ui.WidthLineEdit, QtCore.SIGNAL(_fromUtf8("textEdited(QString)")), self.upload_size_from_width) QtCore.QObject.connect(self.ui.ScaleLineEdit, QtCore.SIGNAL(_fromUtf8("textEdited(QString)")), self.upload_size_from_scale) # endregion QtCore.QObject.connect(self.ui.ZScaleDoubleSpinBox, QtCore.SIGNAL(_fromUtf8("valueChanged(double)")), self.get_height_model) QtCore.QObject.connect(self.ui.BaseHeightLineEdit, QtCore.SIGNAL(_fromUtf8("textEdited(QString)")), self.get_height_model) # region Cancel, export, print buttons QtCore.QObject.connect(self.ui.CancelToolButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.reject) QtCore.QObject.connect(self.ui.STLToolButton, QtCore.SIGNAL(_fromUtf8("clicked()")), self.do_export) close = QtGui.QAction(self) self.connect(close, QtCore.SIGNAL('clicked()'), self.reject) # endregion def do_export(self): parameters = self.get_parameters() layer_name = self.ui.LayerComboBox.currentText() + '_model.stl' if parameters != 0: if parameters["spacing_mm"] < 0.5 and self.height > 100 and self.width > 100: reply = QMessageBox.question(self, self.tr('Export to STL'), self.tr('The construction of the STL file could takes several minutes. Do you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: f = QFileDialog.getSaveFileNameAndFilter(self, self.tr('Export to STL'), layer_name, filter=".stl") stl_file = f[0] if stl_file: export_dlg = Export_dialog.Dialog(parameters, stl_file) if export_dlg.exec_(): QMessageBox.information(self, self.tr("Attention"), self.tr("STL model generated")) else: QMessageBox.information(self, self.tr("Attention"), self.tr("Process canceled")) else: f = QFileDialog.getSaveFileNameAndFilter(self, self.tr('Export to STL'), layer_name, filter=".stl") stl_file = f[0] if stl_file: export_dlg = Export_dialog.Dialog(parameters, stl_file) if export_dlg.exec_(): QMessageBox.information(self, self.tr("Attention"), self.tr("STL model generated")) else: QMessageBox.information(self, self.tr("Attention"), self.tr("Process canceled")) else: QMessageBox.warning(self, self.tr("Attention"), self.tr("Fill the data correctly")) def get_layer(self, layer_name): if self.layer.name() != layer_name: self.ini_dialog() layermap = QgsMapLayerRegistry.instance().mapLayers() for name, layer in layermap.iteritems(): if layer.name() == layer_name: if layer.isValid(): self.layer = layer self.get_raster_properties() else: self.layer = None def ini_dialog(self): self.ui.XMaxLineEdit.clear() self.ui.XMinLineEdit.clear() self.ui.YMaxLineEdit.clear() self.ui.YMinLineEdit.clear() if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None self.ini_dimensions() self.ui.ZMaxLabel.setText('0 m') self.ui.ZMinLabel.setText('0 m') def ini_dimensions(self): self.ui.HeightLineEdit.clear() self.height = 0 self.ui.WidthLineEdit.clear() self.width = 0 self.ui.ScaleLineEdit.clear() self.scale = 0 self.ui.RecomSpacinglabel.setText('0.2 mm') self.ui.BaseHeightLineEdit.clear() self.ui.HeightModelLabel.setText('0 mm') def get_raster_properties(self): self.cell_size = self.layer.rasterUnitsPerPixelX() self.rows = self.layer.height() self.cols = self.layer.width() rec = self.layer.extent() self.raster_x_max = rec.xMaximum() self.raster_x_min = rec.xMinimum() self.raster_y_max = rec.yMaximum() self.raster_y_min = rec.yMinimum() # region Extension functions def full_extent(self): rec = self.canvas.fullExtent() self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() def layer_extent(self): layers = self.iface.legendInterface().layers() select_layer_dialog = SelectLayer_dialog.Dialog(layers) if select_layer_dialog.exec_(): layer = select_layer_dialog.get_layer() if layer: rec = get_layer(layer).extent() source = self.layer.crs() target = self.map_crs if source != target: transform = QgsCoordinateTransform(source, target) rec = transform.transform(rec) self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() def custom_extent(self): self.iface.messageBar().pushMessage("Info", self.tr("Click and drag the mouse to draw print extent"), level=QgsMessageBar.INFO, duration=3) if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None ct = RectangleMapTool(self.canvas, self.get_custom_extent) self.canvas.setMapTool(ct) def get_custom_extent(self, rec): layer_extension = self.layer.extent() source = self.layer.crs() target = self.map_crs if source != target: transform = QgsCoordinateTransform(source, target) layer_extension = transform.transform(layer_extension) if rec.intersects(layer_extension): extension = rec.intersect(layer_extension) self.paint_extent(extension) self.iface.actionPan().trigger() self.get_z_max_z_min() self.ini_dimensions() else: QMessageBox.warning(self, self.tr("Attention"), self.tr("Print extent defined outside layer extent")) def upload_extent(self): try: self.roi_x_max = float(self.ui.XMaxLineEdit.text()) self.roi_x_min = float(self.ui.XMinLineEdit.text()) self.roi_y_max = float(self.ui.YMaxLineEdit.text()) self.roi_y_min = float(self.ui.YMinLineEdit.text()) rec = QgsRectangle(self.roi_x_min, self.roi_y_min, self.roi_x_max, self.roi_y_max) self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() except ValueError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Value entered incorrect")) def paint_extent(self, rec): self.roi_x_max = rec.xMaximum() self.ui.XMaxLineEdit.setText(str(round(rec.xMaximum(), 3))) self.roi_y_min = rec.yMinimum() self.ui.YMinLineEdit.setText(str(round(rec.yMinimum(), 3))) self.roi_x_min = rec.xMinimum() self.ui.XMinLineEdit.setText(str(round(rec.xMinimum(), 3))) self.roi_y_max = rec.yMaximum() self.ui.YMaxLineEdit.setText(str(round(rec.yMaximum(), 3))) if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None self.extent = QgsRubberBand(self.canvas, True) points = [QgsPoint(self.roi_x_max, self.roi_y_min), QgsPoint(self.roi_x_max, self.roi_y_max), QgsPoint(self.roi_x_min, self.roi_y_max), QgsPoint(self.roi_x_min, self.roi_y_min), QgsPoint(self.roi_x_max, self.roi_y_min)] self.extent.setToGeometry(QgsGeometry.fromPolyline(points), None) self.extent.setColor(QColor(227, 26, 28, 255)) self.extent.setWidth(5) self.extent.setLineStyle(Qt.PenStyle(Qt.DashLine)) self.canvas.refresh() def get_z_max_z_min(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) roi = QgsRectangle(self.roi_x_min, self.roi_y_min, self.roi_x_max, self.roi_y_max) source = self.map_crs target = self.layer.crs() transform = QgsCoordinateTransform(source, target) rec = transform.transform(roi) x_max = rec.xMaximum() x_min = rec.xMinimum() y_max = rec.yMaximum() y_min = rec.yMinimum() x_off = int(math.floor((x_min - self.raster_x_min) * self.cols / (self.raster_x_max - self.raster_x_min))) y_off = int(math.floor((self.raster_y_max - y_max) * self.rows / (self.raster_y_max - self.raster_y_min))) col_size = int(math.floor((x_max - x_min) / self.cell_size)) row_size = int(math.floor((y_max - y_min) / self.cell_size)) if x_off < 0: x_off = 0 if y_off < 0: y_off = 0 if x_off >= self.cols: x_off = self.cols - 1 if y_off >= self.rows: y_off = self.rows - 1 if x_off + col_size > self.cols: col_size = self.cols - x_off if row_size + row_size > self.rows: row_size = self.rows - y_off provider = self.layer.dataProvider() path = provider.dataSourceUri() path_layer = path.split('|') dem_dataset = gdal.Open(path_layer[0]) data = self.get_dem_z(dem_dataset, x_off, y_off, col_size, row_size) if data is not None: self.z_max = max(data) self.z_min = self.z_max no_data = dem_dataset.GetRasterBand(1).GetNoDataValue() if min(data) == no_data: for z_cell in data: if z_cell != no_data and z_cell < self.z_min: self.z_min = z_cell elif math.isnan(min(data)): self.z_max = 0 self.z_min = 0 for z_cell in data: if not math.isnan(z_cell): if self.z_min > z_cell: self.z_min = z_cell if self.z_max < z_cell: self.z_max = z_cell else: self.z_min = min(data) if self.z_min < 0: self.z_min = 0 self.z_max = round(self.z_max, 3) self.z_min = round(self.z_min, 3) self.ui.ZMaxLabel.setText(str(self.z_max) + ' m') self.ui.ZMinLabel.setText(str(self.z_min) + ' m') dem_dataset = None band = None QApplication.restoreOverrideCursor() # endregion # region Dimensions function def get_min_spacing(self): min_spacing = 0 if self.map_crs.mapUnits() == 0: # Meters if self.layer.crs().mapUnits() == 0: width_roi = self.roi_x_max - self.roi_x_min min_spacing = round(self.cell_size * self.width / width_roi, 2) elif self.layer.crs().mapUnits() == 2: width_roi = self.roi_x_max - self.roi_x_min cell_size_m = self.cell_size * math.pi / 180 * math.cos(self.roi_y_max * math.pi / 180) * 6371000 min_spacing = round(cell_size_m * self.width / width_roi, 2) # min_spacing = self.cell_size/self.scale elif self.map_crs.mapUnits() == 2: # Degree if self.layer.crs().mapUnits() == 0: width_roi = self.roi_x_max - self.roi_x_min cell_size_deg = self.cell_size / math.pi * 180 / math.cos(self.roi_y_max * math.pi / 180) / 6371000 min_spacing = round(cell_size_deg * self.width / width_roi, 2) elif self.layer.crs().mapUnits() == 2: width_roi = self.roi_x_max - self.roi_x_min min_spacing = round(self.cell_size * self.width / width_roi, 2) if min_spacing < 0.2: self.ui.RecomSpacinglabel.setText('0.2 mm') else: self.ui.RecomSpacinglabel.setText(str(min_spacing) + ' mm') def upload_size_from_height(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.height = float(self.ui.HeightLineEdit.text()) self.width = round(width_roi * self.height / height_roi, 2) self.ui.WidthLineEdit.setText(str(self.width)) if self.map_crs.mapUnits() == 0: # Meters scale1 = height_roi / self.height * 1000 scale2 = width_roi / self.width * 1000 self.scale = round((scale1 + scale2) / 2, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) elif self.map_crs.mapUnits() == 2: # Degree dist = width_roi * math.pi / 180 * math.cos(self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.scale = round(dist / self.width, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define print extent")) self.ui.HeightLineEdit.clear() def upload_size_from_width(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.width = float(self.ui.WidthLineEdit.text()) self.height = round(height_roi * self.width / width_roi, 2) self.ui.HeightLineEdit.setText(str(self.height)) if self.map_crs.mapUnits() == 0: # Meters scale1 = height_roi / self.height * 1000 scale2 = width_roi / self.width * 1000 self.scale = round((scale1 + scale2) / 2, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) elif self.map_crs.mapUnits() == 2: # Degree dist = width_roi * math.pi / 180 * math.cos(self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.scale = round(dist / self.width, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define size model")) self.ui.WidthLineEdit.clear() def upload_size_from_scale(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.scale = float(self.ui.ScaleLineEdit.text()) if self.map_crs.mapUnits() == 0: # Meters self.height = round(height_roi / self.scale * 1000, 2) self.ui.HeightLineEdit.setText(str(self.height)) self.width = round(width_roi / self.scale * 1000, 2) self.ui.WidthLineEdit.setText(str(self.width)) elif self.map_crs.mapUnits() == 2: # Degree dist = width_roi * math.pi / 180 * math.cos(self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.width = round(dist / self.scale, 2) self.ui.WidthLineEdit.setText(str(self.width)) self.height = round(height_roi * self.width / width_roi, 2) self.ui.HeightLineEdit.setText(str(self.height)) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define print extent")) self.ui.ScaleLineEdit.clear() self.scale = 0 self.ui.WidthLineEdit.clear() # endregion def get_height_model(self): if self.ui.BaseHeightLineEdit.text() == '': return try: z_base = float(self.ui.BaseHeightLineEdit.text()) self.z_scale = self.ui.ZScaleDoubleSpinBox.value() h_model = round((self.z_max - z_base) / self.scale * 1000 * self.z_scale + 2, 1) if h_model == float("inf"): QMessageBox.warning(self, self.tr("Attention"), self.tr("Define size model")) self.ui.BaseHeightLineEdit.clear() return if z_base <= self.z_max: self.ui.HeightModelLabel.setText(str(h_model) + ' mm') else: QMessageBox.warning(self, self.tr("Attention"), self.tr("Height of the base must be lower than DEM highest point")) self.ui.BaseHeightLineEdit.clear() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define print extent")) self.ui.BaseHeightLineEdit.clear() def get_parameters(self): if self.map_crs.mapUnits() == 0: # Meters projected = True elif self.map_crs.mapUnits() == 2: # Degree projected = False provider = self.layer.dataProvider() path = provider.dataSourceUri() path_layer = path.split('|') self.z_scale = self.ui.ZScaleDoubleSpinBox.value() try: spacing_mm = float(self.ui.SpacingLineEdit.text()) z_base = float(self.ui.BaseHeightLineEdit.text()) except ValueError: return 0 if self.ui.RevereseZCheckBox.isChecked(): z_inv = True else: z_inv = False return {"layer": path_layer[0], "roi_x_max": self.roi_x_max, "roi_x_min": self.roi_x_min, "roi_y_max": self.roi_y_max, "roi_y_min": self.roi_y_min, "spacing_mm": spacing_mm, "height": self.height, "width": self.width, "z_scale": self.z_scale, "scale": self.scale, "z_inv": z_inv, "z_base": z_base, "projected": projected, "crs_layer": self.layer.crs(), "crs_map": self.map_crs} @staticmethod def get_dem_z(dem_dataset, x_off, y_off, col_size, row_size): band = dem_dataset.GetRasterBand(1) data_types = {'Byte': 'B', 'UInt16': 'H', 'Int16': 'h', 'UInt32': 'I', 'Int32': 'i', 'Float32': 'f', 'Float64': 'd'} data_type = band.DataType data = band.ReadRaster(x_off, y_off, col_size, row_size, col_size, row_size, data_type) data = struct.unpack(data_types[gdal.GetDataTypeName(band.DataType)] * col_size * row_size, data) return data
class PlanetSearchResultsWidget(RESULTS_BASE, RESULTS_WIDGET): """ """ zoomToAOIRequested = pyqtSignal() setAOIRequested = pyqtSignal(dict) setSearchParamsRequested = pyqtSignal(dict, tuple) checkedCountChanged = pyqtSignal(int) grpBoxQuery: QGroupBox teQuery: QPlainTextEdit frameSearching: QFrame btnCancel: QToolButton btnShowQuery: QToolButton btnZoomToAOI: QToolButton btnSetAOI: QToolButton btnSetSearchParams: QToolButton lblSearching: QLabel frameResults: QFrame def __init__(self, parent=None, iface=None, api_key=None, request_type=None, request=None, response_timeout=RESPONSE_TIMEOUT, sort_order=None): super().__init__(parent=parent) self.setupUi(self) self._parent = parent self._iface = iface # TODO: Grab responseTimeOut from plugin settings and override default self._response_timeout = response_timeout self._request_type = request_type self._request = request self.sort_order = sort_order self.teQuery.setPlainText(json.dumps(request, indent=2)) # self.grpBoxQuery.setSaveCollapsedState(False) # self.grpBoxQuery.setCollapsed(True) self.grpBoxQuery.hide() self.btnZoomToAOI.clicked.connect(self._zoom_to_request_aoi) self.btnSetAOI.clicked.connect(self._set_aoi_from_request) self.btnSetSearchParams.clicked.connect(self._set_search_params_from_request) self.btnShowQuery.clicked.connect(self._toggle_query) self._aoi_box = None self._setup_request_aoi_box() self.results_tree = PlanetSearchResultsView( parent=self, iface=iface, api_key=api_key, request_type=request_type, request=request, response_timeout=self._response_timeout, sort_order=sort_order ) self.results_tree.checkedCountChanged[int].connect( self.checked_count_changed) self.frameResults.layout().addWidget(self.results_tree) self.results_tree.setHidden(True) search_model = self.results_tree.search_model() if self.results_tree.search_model(): search_model.searchFinished.connect(self._search_finished) search_model.searchNoResults.connect(self._search_no_results) search_model.searchCancelled.connect(self._search_cancelled) search_model.searchTimedOut[int].connect(self._search_timed_out) self.btnCancel.clicked.connect(search_model.cancel_search) self.waiting_spinner = QtWaitingSpinner( self.frameResults, centerOnParent=True, disableParentWhenSpinning=False, modality=Qt.NonModal ) self.waiting_spinner.setRoundness(80.0) self.waiting_spinner.setMinimumTrailOpacity(15.0) self.waiting_spinner.setTrailFadePercentage(75.0) self.waiting_spinner.setNumberOfLines(15) self.waiting_spinner.setLineLength(14.0) self.waiting_spinner.setLineWidth(3.0) self.waiting_spinner.setInnerRadius(8.0) self.waiting_spinner.setRevolutionsPerSecond(1.0) self.waiting_spinner.setColor(PLANET_COLOR) self.waiting_spinner.start() self.waiting_spinner.show() search_model.start_api_search() # QTimer.singleShot(0, search_model.start_api_search) def _nix_waiting_spinner(self): self.waiting_spinner.stop() self.waiting_spinner.hide() def checked_count(self): return self.results_tree.checked_count() def checked_queue(self): return self.results_tree.checked_queue() @pyqtSlot(int) def checked_count_changed(self, count): if type(self._parent).__name__ == 'QTabWidget': # self._parent: QTabWidget tab_indx = self._parent.indexOf(self) if tab_indx != -1: txt = f'{self._request_type.capitalize()}' if count > 0: txt += f' ({count})' self._parent.setTabText(tab_indx, txt) self.checkedCountChanged.emit(count) @pyqtSlot() def _toggle_query(self): if self.grpBoxQuery.isVisible(): self.grpBoxQuery.hide() self.btnShowQuery.setIcon( QIcon(':/plugins/planet_explorer/expand-triangle.svg') ) else: self.grpBoxQuery.show() self.btnShowQuery.setIcon( QIcon(':/plugins/planet_explorer/collapse-triangle.svg') ) @pyqtSlot() def _search_finished(self): self._nix_waiting_spinner() self.frameSearching.hide() self.results_tree.show() @pyqtSlot() def _search_no_results(self): self._nix_waiting_spinner() self.lblSearching.setText('No results found') self.btnCancel.hide() self.results_tree.hide() @pyqtSlot() def _search_cancelled(self): self._nix_waiting_spinner() self.lblSearching.setText('Search cancelled') self.btnCancel.hide() self.results_tree.hide() @pyqtSlot(int) def _search_timed_out(self, timeout): self._nix_waiting_spinner() self.lblSearching.setText(f'Search timed out ({timeout} seconds)') self.btnCancel.hide() self.results_tree.hide() def _setup_request_aoi_box(self): if self._iface: log.debug('iface is available, adding aoi box support') self._aoi_box = QgsRubberBand( self._iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self._aoi_box.setFillColor(QColor(0, 0, 0, 0)) self._aoi_box.setStrokeColor(SEARCH_AOI_COLOR) self._aoi_box.setWidth(2) self._aoi_box.setLineStyle(Qt.DashLine) else: log.debug('iface is None, skipping footprint support') self._aoi_box = None @pyqtSlot() def clear_aoi_box(self): if self._aoi_box: self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) @pyqtSlot() def _zoom_to_request_aoi(self): if not self._iface: log.debug('No iface object, skipping AOI extent') return aoi_geom = None if self._request_type == RESOURCE_DAILY: aoi_geom = geometry_from_request(self._request) if not aoi_geom: log.debug('No AOI geometry defined, skipping zoom to AOI') return qgs_geom: QgsGeometry = qgsgeometry_from_geojson(aoi_geom) self._aoi_box.setToGeometry( qgs_geom, QgsCoordinateReferenceSystem("EPSG:4326") ) self.show_aoi() zoom_canvas_to_aoi(aoi_geom, iface_obj=self._iface) self.zoomToAOIRequested.emit() def hide_aoi_if_matches_geom(self,geom): if self._aoi_box is not None: color = (QColor(0, 0, 0, 0) if self._aoi_box.asGeometry().equals(geom) else SEARCH_AOI_COLOR) self._aoi_box.setStrokeColor(color) def show_aoi(self): if self._aoi_box is not None: self._aoi_box.setStrokeColor(SEARCH_AOI_COLOR) def aoi_geom(self): if self._aoi_box is not None: return self._aoi_box.asGeometry() @pyqtSlot() def _set_aoi_from_request(self): self.setAOIRequested.emit(self._request) @pyqtSlot() def _set_search_params_from_request(self): self.setSearchParamsRequested.emit(self._request, self.sort_order) @pyqtSlot() def clean_up(self): self.results_tree.clean_up() self.clear_aoi_box() # noinspection PyPep8Naming def closeEvent(self, event): self.clean_up() super().closeEvent(self, event) def request_query(self): return self._request
class RectangleMapTool(QgsMapTool): def __init__(self, canvas, callback): self.canvas = canvas QgsMapTool.__init__(self, self.canvas) self.callback = callback self.rubberBand = QgsRubberBand(self.canvas, True) self.rubberBand.setColor(QColor(227, 26, 28, 255)) self.rubberBand.setWidth(5) self.rubberBand.setLineStyle(Qt.PenStyle(Qt.DashLine)) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(True) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False r = self.rectangle() if r is not None: # print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() self.rubberBand.hide() self.callback(r) # self.deactivate() return None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(True) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, False) self.rubberBand.addPoint(point1, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def deactivate(self): super(RectangleMapTool, self).deactivate() self.emit(QtCore.SIGNAL("deactivated()"))
class MoveTool(QgsMapTool): def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.__canvas = iface.mapCanvas() self.__icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.__text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = 0 self.__findVertex = 0 self.__onMove = 0 self.__counter = 0 self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None self.__layerConfig = None def icon_path(self): """ To get the icon path :return: icon path """ return self.__icon_path def text(self): """ To get the menu text :return: menu text """ return self.__text def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__updateList() self.__canvas.layersChanged.connect(self.__updateList) QgsProject.instance().snapSettingsChanged.connect(self.__updateList) def deactivate(self): """ When the action is deselected """ self.__canvas.layersChanged.disconnect(self.__updateList) QgsProject.instance().snapSettingsChanged.disconnect(self.__updateList) QgsMapTool.deactivate(self) def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) self.__layer.editingStarted.disconnect(self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) self.__layer.editingStopped.disconnect(self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.__canvas.setMapTool(self) def removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None\ and isinstance(layer, QgsVectorLayer): if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): self.__layer.editingStopped.disconnect(self.stopEditing) else: self.__layer.editingStarted.disconnect(self.startEditing) self.__layer = layer if self.__layer.isEditable(): self.action().setEnabled(True) self.__updateList() self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.__canvas.mapTool == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) self.removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry()) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry()) print(self.__selectedVertex) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in xrange(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry( QgsGeometry(self.__newFeature.curveToLine()), None) def __newCurve(self, curved, line_v2, dx, dy): if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in xrange(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2( self.__selectedFeature.geometry()) vertex = polygon_v2.vertexAt(self.__polygonVertexId(polygon_v2)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line) line_v2 = self.__newLine(polygon_v2.exteriorRing(), dx, dy, curved[0]) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in xrange(polygon_v2.numInteriorRings()): line_v2 = self.__newLine(polygon_v2.interiorRing(num), dx, dy, curved[num + 1]) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __polygonVertexId(self, polygon_v2): """ To get the id of the selected vertex from a polygon :param polygon_v2: the polygon as polygonV2 :return: id as QgsVertexId """ eR = polygon_v2.exteriorRing() if self.__selectedVertex < eR.numPoints(): return QgsVertexId(0, 0, self.__selectedVertex, 1) else: sel = self.__selectedVertex - eR.numPoints() for num in xrange(polygon_v2.numInteriorRings()): iR = polygon_v2.interiorRing(num) if sel < iR.numPoints(): return QgsVertexId(0, num + 1, sel, 1) sel -= iR.numPoints() def __newLine(self, curve_v2, dx, dy, curved): """ To create a new moved line for a part of a polygon :param curve_v2: the original line :param dx: x translation :param dy: y translation :return: the line as lineV2 """ if curved: new_line_v2 = QgsCircularStringV2() else: new_line_v2 = QgsLineStringV2() points = [] for pos in xrange(curve_v2.numPoints()): x = curve_v2.pointN(pos).x() - dx y = curve_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(curve_v2.pointN(pos).z()) points.append(pt) new_line_v2.setPoints(points) return new_line_v2 def __onConfirmClose(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.close() self.__rubberBand.reset() self.__rubberSnap.reset() self.__isEditing = 0 self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__layer.updateExtents() self.__onConfirmClose() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute( field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and \ self.__layer.editFormConfig().suppress() != QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__layer.updateExtents() self.__onConfirmClose() def __updateList(self): """ To update the snapping options of the layer """ noUse, enabled, snappingType, unitType, tolerance, avoidIntersection = \ QgsProject.instance().snapSettingsForLayer(self.__layer.id()) self.__layerConfig = QgsSnappingUtils.LayerConfig( self.__layer, QgsPointLocator.Vertex, tolerance, unitType) if not enabled or tolerance == 0: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Error"), QCoreApplication.translate( "VDLTools", "This layer has no snapping options"), level=QgsMessageBar.CRITICAL) def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: f = Finder.findClosestFeatureAt(event.mapPoint(), self.__layerConfig, self) if f is not None and self.__lastFeatureId != f.id(): self.__lastFeatureId = f.id() self.__layer.setSelectedFeatures([f.id()]) if f is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry( QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(event.mapPoint()) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(event.mapPoint()) else: self.__pointPreview(event.mapPoint()) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) if self.__rubberSnap: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(event.mapPoint(), self.__canvas, True) if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) else: self.__rubberSnap.setIcon(1) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) if match.hasEdge(): self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry( QgsGeometry().fromPoint(match.point()), None) # if self.__counter > 2: # if self.__rubberSnap: # self.__rubberSnap.reset() # else: # self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point) # self.__rubberSnap.setColor(color) # self.__rubberSnap.setWidth(2) # self.__rubberSnap.setIconSize(20) # snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList) # if snappedIntersection is None: # snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList) # if snappedPoint is not None: # self.__rubberSnap.setIcon(4) # self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedPoint), None) # else: # self.__rubberSnap.setIcon(1) # self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedIntersection), None) # self.__counter = 0 # else: # self.__counter += 1 def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) < 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__findVertex = 1 self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Point) else: self.__onMove = 1 # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface) elif self.__findVertex: self.__findVertex = 0 closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = 1 # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface) elif self.__onMove: self.__onMove = 0 mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.__canvas, True) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() # snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList) # if snappedIntersection is None: # snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList) # if snappedPoint is not None: # mapPoint = snappedPoint # else: # mapPoint = snappedIntersection self.__isEditing = 1 if self.__rubberBand: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmClose) self.__confDlg.show()
class DEMto3DDialog(QDialog, Ui_DEMto3DDialogBase): # Layer to print layer = None ''' Region of interest properties ''' map_crs = None roi_x_max = 0 roi_x_min = 0 roi_y_max = 0 roi_y_min = 0 z_max = 0 z_min = 0 ''' Model dimensions ''' height = 0 width = 0 scale = 0 z_scale = 0 ''' Raster properties ''' cell_size = 0 cols = 0 rows = 0 raster_x_max = 0 raster_x_min = 0 raster_y_max = 0 raster_y_min = 0 def __init__(self, iface): """Constructor.""" QDialog.__init__(self) self.ui = Ui_DEMto3DDialogBase() self.ui.setupUi(self) self.iface = iface self.canvas = iface.mapCanvas() try: self.map_crs = self.canvas.mapSettings().destinationCrs() except: self.map_crs = self.canvas.mapRenderer().destinationCrs() # region Layer action # fill layer combobox with raster visible layers in mapCanvas self.viewLayers = self.canvas.layers() self.ui.LayerComboBox.clear() for layer in self.viewLayers: if layer.type() == 1: self.ui.LayerComboBox.addItem(layer.name()) self.layer = get_layer(self.ui.LayerComboBox.currentText()) self.get_raster_properties() self.ui.LayerComboBox.activated.connect(self.get_layer) # endregion # region Extension actions self.extent = None self.ui.FullExtToolButton.clicked.connect(self.full_extent) self.ui.LayerExtToolButton.clicked.connect(self.layer_extent) self.ui.CustomExtToolButton.clicked.connect(self.custom_extent) self.ui.XMinLineEdit.returnPressed.connect(self.upload_extent) self.ui.XMaxLineEdit.returnPressed.connect(self.upload_extent) self.ui.YMaxLineEdit.returnPressed.connect(self.upload_extent) self.ui.YMinLineEdit.returnPressed.connect(self.upload_extent) # endregion # region Dimension actions self.ui.HeightLineEdit.textEdited.connect(self.upload_size_from_height) self.ui.WidthLineEdit.textEdited.connect(self.upload_size_from_width) self.ui.ScaleLineEdit.textEdited.connect(self.upload_size_from_scale) # endregion self.ui.ZScaleDoubleSpinBox.valueChanged.connect(self.get_height_model) self.ui.BaseHeightLineEdit.textEdited.connect(self.get_height_model) # region Cancel, export, print buttons self.ui.CancelToolButton.clicked.connect(self.reject) self.ui.STLToolButton.clicked.connect(self.do_export) """ close is not print, QtGui.QAction doesn't exist anymore..""" # close = QtGui.QAction(self) # close.connect(self.reject) # endregion def do_export(self): parameters = self.get_parameters() layer_name = self.ui.LayerComboBox.currentText() + '_model.stl' if parameters != 0: reply = QMessageBox.question( self, self.tr('Export to STL'), self.tr('The STL model will be written to "' + layer_name + '", change?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: stl_file, _ = QFileDialog.getSaveFileName( self, self.tr('Export to STL'), layer_name, filter=".stl") else: stl_file = layer_name if stl_file: export_dlg = Export_dialog.Dialog(parameters, stl_file) if export_dlg.exec_(): QMessageBox.information(self, self.tr("Success"), self.tr("STL model generated")) else: QMessageBox.information(self, self.tr("Attention"), self.tr("Process canceled")) else: QMessageBox.warning( self, self.tr("Attention"), self.tr( "Fill parameters (extend, width, height, offset) correctly" )) def get_layer(self, layer_name): if self.layer.name() != layer_name: self.ini_dialog() layermap = QgsProject.instance().mapLayers() for name, layer in list(layermap.items()): if layer.name() == layer_name: if layer.isValid(): self.layer = layer self.iface.layerTreeView().setCurrentLayer(layer) self.get_raster_properties() else: self.layer = None def ini_dialog(self): self.ui.XMaxLineEdit.clear() self.ui.XMinLineEdit.clear() self.ui.YMaxLineEdit.clear() self.ui.YMinLineEdit.clear() if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None self.ini_dimensions() self.ui.ZMaxLabel.setText('0 m') self.ui.ZMinLabel.setText('0 m') def ini_dimensions(self): self.ui.HeightLineEdit.clear() self.height = 0 self.ui.WidthLineEdit.clear() self.width = 0 self.ui.ScaleLineEdit.clear() self.scale = 0 self.ui.RecomSpacinglabel.setText('0.2 mm') self.ui.BaseHeightLineEdit.clear() self.ui.HeightModelLabel.setText('0 mm') def get_raster_properties(self): self.cell_size = self.layer.rasterUnitsPerPixelX() self.rows = self.layer.height() self.cols = self.layer.width() rec = self.layer.extent() self.raster_x_max = rec.xMaximum() self.raster_x_min = rec.xMinimum() self.raster_y_max = rec.yMaximum() self.raster_y_min = rec.yMinimum() # region Extension functions def full_extent(self): rec = self.canvas.fullExtent() self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() def layer_extent(self): """ if layer extent is choosen, there is little need to select another layer extend """ layers = self.iface.layerTreeView().selectedLayers() select_layer_dialog = SelectLayer_dialog.Dialog(layers) if select_layer_dialog.exec_(): layer = select_layer_dialog.get_layer() if layer: rec = get_layer(layer).extent() source = self.layer.crs() target = self.map_crs if source != target: transform = QgsCoordinateTransform(source, target) rec = transform.transform(rec) self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() def custom_extent(self): self.iface.messageBar().pushMessage( "Info", self.tr("Click and drag the mouse to draw print extent"), level=QgsMessageBar.INFO, duration=3) if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None ct = RectangleMapTool(self.canvas, self.get_custom_extent) self.canvas.setMapTool(ct) def get_custom_extent(self, rec): layer_extension = self.layer.extent() source = self.layer.crs() target = self.map_crs if source != target: transform = QgsCoordinateTransform(source, target) layer_extension = transform.transform(layer_extension) if rec.intersects(layer_extension): extension = rec.intersect(layer_extension) self.paint_extent(extension) self.iface.actionPan().trigger() self.get_z_max_z_min() self.ini_dimensions() else: QMessageBox.warning( self, self.tr("Attention"), self.tr("Print extent defined outside layer extent")) def upload_extent(self): try: self.roi_x_max = float(self.ui.XMaxLineEdit.text()) self.roi_x_min = float(self.ui.XMinLineEdit.text()) self.roi_y_max = float(self.ui.YMaxLineEdit.text()) self.roi_y_min = float(self.ui.YMinLineEdit.text()) rec = QgsRectangle(self.roi_x_min, self.roi_y_min, self.roi_x_max, self.roi_y_max) self.paint_extent(rec) self.get_z_max_z_min() self.ini_dimensions() except ValueError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Value entered incorrect")) def paint_extent(self, rec): self.roi_x_max = rec.xMaximum() self.ui.XMaxLineEdit.setText(str(round(rec.xMaximum(), 3))) self.roi_y_min = rec.yMinimum() self.ui.YMinLineEdit.setText(str(round(rec.yMinimum(), 3))) self.roi_x_min = rec.xMinimum() self.ui.XMinLineEdit.setText(str(round(rec.xMinimum(), 3))) self.roi_y_max = rec.yMaximum() self.ui.YMaxLineEdit.setText(str(round(rec.yMaximum(), 3))) if self.extent: self.canvas.scene().removeItem(self.extent) self.extent = None self.extent = QgsRubberBand(self.canvas, True) points = [ QgsPoint(self.roi_x_max, self.roi_y_min), QgsPoint(self.roi_x_max, self.roi_y_max), QgsPoint(self.roi_x_min, self.roi_y_max), QgsPoint(self.roi_x_min, self.roi_y_min), QgsPoint(self.roi_x_max, self.roi_y_min) ] self.extent.setToGeometry(QgsGeometry.fromPolyline(points), None) self.extent.setColor(QColor(227, 26, 28, 255)) self.extent.setWidth(5) self.extent.setLineStyle(Qt.PenStyle(Qt.DashLine)) self.canvas.refresh() def get_z_max_z_min(self): QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) roi = QgsRectangle(self.roi_x_min, self.roi_y_min, self.roi_x_max, self.roi_y_max) source = self.map_crs target = self.layer.crs() transform = QgsCoordinateTransform(source, target, QgsProject.instance()) rec = transform.transform(roi) x_max = rec.xMaximum() x_min = rec.xMinimum() y_max = rec.yMaximum() y_min = rec.yMinimum() x_off = int( math.floor((x_min - self.raster_x_min) * self.cols / (self.raster_x_max - self.raster_x_min))) y_off = int( math.floor((self.raster_y_max - y_max) * self.rows / (self.raster_y_max - self.raster_y_min))) col_size = int(math.floor((x_max - x_min) / self.cell_size)) row_size = int(math.floor((y_max - y_min) / self.cell_size)) if x_off < 0: x_off = 0 if y_off < 0: y_off = 0 if x_off >= self.cols: x_off = self.cols - 1 if y_off >= self.rows: y_off = self.rows - 1 if x_off + col_size > self.cols: col_size = self.cols - x_off if row_size + row_size > self.rows: row_size = self.rows - y_off provider = self.layer.dataProvider() path = provider.dataSourceUri() path_layer = path.split('|') dem_dataset = gdal.Open(path_layer[0]) data = self.get_dem_z(dem_dataset, x_off, y_off, col_size, row_size) if data is not None: self.z_max = max(data) self.z_min = self.z_max no_data = dem_dataset.GetRasterBand(1).GetNoDataValue() if min(data) == no_data: for z_cell in data: if z_cell != no_data and z_cell < self.z_min: self.z_min = z_cell elif math.isnan(min(data)): self.z_max = 0 self.z_min = 0 for z_cell in data: if not math.isnan(z_cell): if self.z_min > z_cell: self.z_min = z_cell if self.z_max < z_cell: self.z_max = z_cell else: self.z_min = min(data) if self.z_min < 0: self.z_min = 0 self.z_max = round(self.z_max, 3) self.z_min = round(self.z_min, 3) self.ui.ZMaxLabel.setText(str(self.z_max) + ' m') self.ui.ZMinLabel.setText(str(self.z_min) + ' m') dem_dataset = None band = None QApplication.restoreOverrideCursor() # endregion # region Dimensions function def get_min_spacing(self): min_spacing = 0.2 if QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'meters': width_roi = self.roi_x_max - self.roi_x_min min_spacing = round(self.cell_size * self.width / width_roi, 2) elif QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'degrees': width_roi = self.roi_x_max - self.roi_x_min min_spacing = round(self.cell_size * self.width / width_roi, 2) self.ui.SpacingLineEdit.setText(str(float(min_spacing))) if min_spacing < 0.2: self.ui.RecomSpacinglabel.setText('0.2 mm') else: self.ui.RecomSpacinglabel.setText(str(min_spacing) + ' mm') def upload_size_from_height(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.height = float(self.ui.HeightLineEdit.text()) self.width = round(width_roi * self.height / height_roi, 2) self.ui.WidthLineEdit.setText(str(self.width)) if QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'meters': scale1 = height_roi / self.height * 1000 scale2 = width_roi / self.width * 1000 self.scale = round((scale1 + scale2) / 2, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) elif QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'degrees': dist = width_roi * math.pi / 180 * math.cos( self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.scale = round(dist / self.width, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define height extent")) self.ui.HeightLineEdit.clear() def upload_size_from_width(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.width = float(self.ui.WidthLineEdit.text()) self.height = round(height_roi * self.width / width_roi, 2) self.ui.HeightLineEdit.setText(str(self.height)) if QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'meters': scale1 = height_roi / self.height * 1000 scale2 = width_roi / self.width * 1000 self.scale = round((scale1 + scale2) / 2, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) elif QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'degrees': dist = width_roi * math.pi / 180 * math.cos( self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.scale = round(dist / self.width, 6) self.ui.ScaleLineEdit.setText(str(int(self.scale))) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define width extent")) self.ui.WidthLineEdit.clear() def upload_size_from_scale(self): try: width_roi = self.roi_x_max - self.roi_x_min height_roi = self.roi_y_max - self.roi_y_min self.scale = float(self.ui.ScaleLineEdit.text()) if QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'meters': self.height = round(height_roi / self.scale * 1000, 2) self.ui.HeightLineEdit.setText(str(self.height)) self.width = round(width_roi / self.scale * 1000, 2) self.ui.WidthLineEdit.setText(str(self.width)) elif QgsUnitTypes.toString(self.map_crs.mapUnits()) == 'degrees': dist = width_roi * math.pi / 180 * math.cos( self.roi_y_max * math.pi / 180) * 6371000 * 1000 self.width = round(dist / self.scale, 2) self.ui.WidthLineEdit.setText(str(self.width)) self.height = round(height_roi * self.width / width_roi, 2) self.ui.HeightLineEdit.setText(str(self.height)) self.get_min_spacing() self.get_height_model() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define scale extent")) self.ui.ScaleLineEdit.clear() self.scale = 0 self.ui.WidthLineEdit.clear() # endregion def get_height_model(self): if self.ui.BaseHeightLineEdit.text() == '': return try: z_base = float(self.ui.BaseHeightLineEdit.text()) self.z_scale = self.ui.ZScaleDoubleSpinBox.value() h_model = round( (self.z_max - z_base) / self.scale * 1000 * self.z_scale + 2, 1) if h_model == float("inf"): QMessageBox.warning(self, self.tr("Attention"), self.tr("Define size model")) self.ui.BaseHeightLineEdit.clear() return if z_base <= self.z_max: self.ui.HeightModelLabel.setText(str(h_model) + ' mm') else: QMessageBox.warning( self, self.tr("Attention"), self. tr("Height of the base must be lower than DEM highest point" )) self.ui.BaseHeightLineEdit.clear() except ZeroDivisionError: QMessageBox.warning(self, self.tr("Attention"), self.tr("Define print extent")) self.ui.BaseHeightLineEdit.clear() def get_parameters(self): projected = False if self.map_crs.mapUnits() == 0: # Meters projected = True provider = self.layer.dataProvider() path = provider.dataSourceUri() path_layer = path.split('|') self.z_scale = self.ui.ZScaleDoubleSpinBox.value() try: spacing_mm = float(self.ui.SpacingLineEdit.text()) z_base = float(self.ui.BaseHeightLineEdit.text()) except ValueError: return 0 if self.ui.RevereseZCheckBox.isChecked(): z_inv = True else: z_inv = False return { "layer": path_layer[0], "roi_x_max": self.roi_x_max, "roi_x_min": self.roi_x_min, "roi_y_max": self.roi_y_max, "roi_y_min": self.roi_y_min, "spacing_mm": spacing_mm, "height": self.height, "width": self.width, "z_scale": self.z_scale, "scale": self.scale, "z_inv": z_inv, "z_base": z_base, "projected": projected, "crs_layer": self.layer.crs(), "crs_map": self.map_crs } @staticmethod def get_dem_z(dem_dataset, x_off, y_off, col_size, row_size): band = dem_dataset.GetRasterBand(1) data_types = { 'Byte': 'B', 'UInt16': 'H', 'Int16': 'h', 'UInt32': 'I', 'Int32': 'i', 'Float32': 'f', 'Float64': 'd' } data_type = band.DataType data = band.ReadRaster(x_off, y_off, col_size, row_size, col_size, row_size, data_type) data = struct.unpack( data_types[gdal.GetDataTypeName(band.DataType)] * col_size * row_size, data) return data
class MoveTool(QgsMapToolAdvancedDigitizing): """ Map tool class to move or copy an object """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__findVertex = False self.__onMove = False self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def deactivate(self): """ When the action is deselected """ self.__cancel() QgsMapToolAdvancedDigitizing.deactivate(self) def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def __cancel(self): """ To cancel used variables """ if self.__rubberBand is not None: self.canvas().scene().removeItem(self.__rubberBand) self.__rubberBand.reset() self.__rubberBand = None if self.__rubberSnap is not None: self.canvas().scene().removeItem(self.__rubberSnap) self.__rubberSnap.reset() self.__rubberSnap = None self.__isEditing = False self.__findVertex = False self.__onMove = False self.__lastFeatureId = None self.__selectedFeature = None self.__confDlg = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.__removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in range(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry(QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.curveToLine()), None) @staticmethod def __newCurve(curved, line_v2, dx, dy): """ To create a new moved line :param curved: if the line is curved :param line_v2: the original line :param dx: x translation :param dy: y translation :return: the new line """ if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in range(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2(self.__selectedFeature.geometry(), self.__iface) vertex = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in range(polygon_v2.numInteriorRings()): line_v2 = self.__newCurve(curved[num+1], polygon_v2.interiorRing(num), dx, dy) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __onConfirmCancel(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__confDlg.accept() self.__cancel() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute(field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \ QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__confDlg.accept() self.__cancel() def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and not self.__onMove: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings]) if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: if self.__rubberBand is not None: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry(QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(map_point) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(map_point) else: self.__pointPreview(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(8) if self.__rubberSnap is not None: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(map_point, self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) else: self.__rubberSnap.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections(point, self.canvas(), self) if intersection is not None: self.__rubberSnap.setIcon(1) point = intersection else: self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Select vertex for moving (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.__findVertex = True self.setMode(self.CaptureLine) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) else: self.setMode(self.CaptureNone) self.__onMove = True elif self.__findVertex: self.__findVertex = False self.setMode(self.CaptureNone) closest = self.__selectedFeature.geometry().closestVertex(event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = True elif self.__onMove: self.__onMove = False mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections(mapPoint, self.canvas(), self) if intersection is not None: mapPoint = intersection self.__isEditing = True if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel) self.__confDlg.show()
class VoGISProfilToolMainDialog(QDialog): def __init__(self, interface, settings): self.settings = settings self.iface = interface self.selectingVisibleRasters = False self.thread = None QDialog.__init__(self, interface.mainWindow()) # Set up the user interface from Designer. self.ui = Ui_VoGISProfilToolMain() self.ui.setupUi(self) if self.settings.onlyHektoMode is True: self.ui.IDC_widRaster.hide() self.adjustSize() validator = QIntValidator(-32768, 32768, self) self.ui.IDC_tbNoDataExport.setValidator(validator) self.ui.IDC_tbFromX.setText('-30000') self.ui.IDC_tbFromY.setText('240000') self.ui.IDC_tbToX.setText('-20000') self.ui.IDC_tbToY.setText('230000') self.__addRastersToGui() self.__addPolygonsToGui() for line_lyr in self.settings.mapData.lines.lines(): self.ui.IDC_cbLineLayers.addItem(line_lyr.name, line_lyr) if self.settings.mapData.lines.count() < 1: self.ui.IDC_rbDigi.setChecked(True) self.ui.IDC_rbShapeLine.setEnabled(False) #Einstellungen fuer Linie zeichen self.action = QAction(QIcon(":/plugins/vogisprofiltoolmain/icons/icon.png"), "VoGIS-Profiltool", self.iface.mainWindow()) self.action.setWhatsThis("VoGIS-Profiltool") self.canvas = self.iface.mapCanvas() self.tool = ProfiletoolMapTool(self.canvas, self.action) self.savedTool = self.canvas.mapTool() self.polygon = False self.rubberband = QgsRubberBand(self.canvas, self.polygon) if QGis.QGIS_VERSION_INT >= 10900: #self.rubberband.setBrushStyle() self.rubberband.setLineStyle(Qt.SolidLine) self.rubberband.setWidth(4.0) self.rubberband.setColor(QColor(0, 255, 0)) #http://www.qgis.org/api/classQgsRubberBand.html#a6f7cdabfcf69b65dfc6c164ce2d01fab self.pointsToDraw = [] self.dblclktemp = None self.drawnLine = None def accept(self): try: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "ACCEPTED") #QgsMessageLog.logMessage('nodata: {0}'.format(self.settings.nodata_value), 'VoGis') self.settings.nodata_value = int(self.ui.IDC_tbNoDataExport.text()) QgsMessageLog.logMessage('maindlg: nodata: {0}'.format(self.settings.nodata_value), 'VoGis') if self.settings.onlyHektoMode is True and self.settings.mapData.rasters.count() > 0: self.settings.onlyHektoMode = False if self.settings.onlyHektoMode is False: if self.settings.mapData.rasters.count() < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", u"Keine Raster vorhanden. Zum Hektometrieren Dialog neu öffnen.") #return retVal = QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Rasterebene vorhanden oder sichtbar! Nur hektometrieren?', None, QApplication.UnicodeUTF8), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if retVal == QMessageBox.No: return else: self.settings.onlyHektoMode = True self.settings.createHekto = True if self.__getSettingsFromGui() is False: return if self.settings.onlyHektoMode is False: if len(self.settings.mapData.rasters.selectedRasters()) < 1: #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", "Kein Raster selektiert!") #msg = #QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", msg) QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Kein Raster selektiert!', None, QApplication.UnicodeUTF8)) return QgsMessageLog.logMessage('modeLine!=line: {0}'.format(self.settings.modeLine != enumModeLine.line), 'VoGis') QgsMessageLog.logMessage('customLine is None: {0}'.format(self.settings.mapData.customLine is None), 'VoGis') if self.settings.modeLine != enumModeLine.line and self.settings.mapData.customLine is None: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Keine Profillinie vorhanden!', None, QApplication.UnicodeUTF8)) return if len(self.settings.mapData.polygons.selected_polygons()) > 0 and len(self.settings.mapData.rasters.selectedRasters()) > 1: raster_names = list(raster.name for raster in self.settings.mapData.rasters.selectedRasters()) sel_raster, ok_clicked = QInputDialog.getItem( self.iface.mainWindow(), u'DHM?', u'Welches DHM soll zur Flächenverschneidung verwendet werden?', raster_names, 0, False ) if ok_clicked is False: return self.settings.intersection_dhm_idx = raster_names.index(sel_raster) #self.rubberband.reset(self.polygon) #QDialog.accept(self) QApplication.setOverrideCursor(Qt.WaitCursor) create_profile = CreateProfile(self.iface, self.settings) thread = QThread(self) create_profile.moveToThread(thread) create_profile.finished.connect(self.profiles_finished) create_profile.error.connect(self.profiles_error) create_profile.progress.connect(self.profiles_progress) thread.started.connect(create_profile.create) thread.start(QThread.LowestPriority) self.thread = thread self.create_profile = create_profile self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) except: QApplication.restoreOverrideCursor() ex = u'{0}'.format(traceback.format_exc()) msg = 'Unexpected ERROR:\n\n{0}'.format(ex[:2000]) QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", msg) def profiles_finished(self, profiles, intersections): QApplication.restoreOverrideCursor() self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) #self.create_profile.deleteLater() self.thread.quit() self.thread.wait() #self.thread.deleteLater() #QGIS 2.0 http://gis.stackexchange.com/a/58754 http://gis.stackexchange.com/a/57090 self.iface.mainWindow().statusBar().showMessage('VoGIS-Profiltool, {0} Profile'.format(len(profiles))) QgsMessageLog.logMessage(u'Profile Count: {0}'.format(len(profiles)), 'VoGis') if len(profiles) < 1: QApplication.restoreOverrideCursor() QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', 'Es konnten keine Profile erstellt werden.', None, QApplication.UnicodeUTF8)) return dlg = VoGISProfilToolPlotDialog(self.iface, self.settings, profiles, intersections) dlg.show() #result = self.dlg.exec_() dlg.exec_() def profiles_error(self, exception_string): QApplication.restoreOverrideCursor() QgsMessageLog.logMessage(u'Error during profile creation: {0}'.format(exception_string), 'VoGis') QMessageBox.critical(self.iface.mainWindow(), "VoGIS-Profiltool", exception_string) def profiles_progress(self, msg): self.iface.mainWindow().statusBar().showMessage(msg) self.ui.IDC_lblCreateStatus.setText(msg) #QgsMessageLog.logMessage(msg, 'VoGis') QApplication.processEvents() def reject(self): if not self.thread is None: if self.thread.isRunning(): self.create_profile.abort() return self.rubberband.reset(self.polygon) QDialog.reject(self) def selectVisibleRasters(self): self.refreshRasterList() self.selectingVisibleRasters = True extCanvas = self.iface.mapCanvas().extent() #alle raster in den einstellunge deselektieren for r in self.settings.mapData.rasters.rasters(): r.selected = False #alle raster in der ListView deselektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) item.setCheckState(Qt.Unchecked) #Raster im Extent selektieren for idx in xrange(self.ui.IDC_listRasters.count()): item = self.ui.IDC_listRasters.item(idx) if QGis.QGIS_VERSION_INT < 10900: raster = item.data(Qt.UserRole).toPyObject() else: raster = item.data(Qt.UserRole) for r in self.settings.mapData.rasters.rasters(): if extCanvas.intersects(r.grid.extent()): if r.id == raster.id: r.selected = True item.setCheckState(Qt.Checked) self.selectingVisibleRasters = False def lineLayerChanged(self, idx): if self.ui.IDC_rbShapeLine.isChecked() is False: self.ui.IDC_rbShapeLine.setChecked(True) if QGis.QGIS_VERSION_INT < 10900: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex()).toPyObject()) else: lineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) lyr = lineLyr.line #QgsMessageLog.logMessage('{0}'.format(lyr.selectedFeatureCount()), 'VoGis') #QgsMessageLog.logMessage('{0}'.format(dir(lyr)), 'VoGis') if hasattr(lyr, 'selectedFeatureCount'): if(lyr.selectedFeatureCount() < 1): self.ui.IDC_chkOnlySelectedFeatures.setChecked(False) else: self.ui.IDC_chkOnlySelectedFeatures.setChecked(True) def valueChangedEquiDistance(self, val): if self.ui.IDC_rbEquiDistance.isChecked() is False: self.ui.IDC_rbEquiDistance.setChecked(True) def valueChangedVertexCount(self, val): if self.ui.IDC_rbVertexCount.isChecked() is False: self.ui.IDC_rbVertexCount.setChecked(True) def lvRasterItemChanged(self, item): if self.selectingVisibleRasters is True: return if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: raster_lyr = item_data.toPyObject() else: raster_lyr = item_data self.settings.mapData.rasters.getById(raster_lyr.id).selected = selected def lvPolygonItemChanged(self, item): if item.checkState() == Qt.Checked: selected = True if item.checkState() == Qt.Unchecked: selected = False item_data = item.data(Qt.UserRole) if QGis.QGIS_VERSION_INT < 10900: poly_lyr = item_data.toPyObject() else: poly_lyr = item_data self.settings.mapData.polygons.getById(poly_lyr.id).selected = selected def refreshRasterList(self): legend = self.iface.legendInterface() avail_lyrs = legend.layers() raster_coll = RasterCollection() for lyr in avail_lyrs: if legend.isLayerVisible(lyr): lyr_type = lyr.type() lyr_name = unicodedata.normalize('NFKD', unicode(lyr.name())).encode('ascii', 'ignore') if lyr_type == 1: if lyr.bandCount() < 2: new_raster = Raster(lyr.id(), lyr_name, lyr) raster_coll.addRaster(new_raster) self.settings.mapData.rasters = raster_coll self.__addRastersToGui() def __addRastersToGui(self): self.ui.IDC_listRasters.clear() check = Qt.Unchecked if self.settings.mapData.rasters.count() == 1: check = Qt.Checked self.settings.mapData.rasters.rasters()[0].selected = True for raster_lyr in self.settings.mapData.rasters.rasters(): item = QListWidgetItem(raster_lyr.name) item.setData(Qt.UserRole, raster_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listRasters.addItem(item) def __addPolygonsToGui(self): self.ui.IDC_listPolygons.clear() check = Qt.Unchecked for poly_lyr in self.settings.mapData.polygons.polygons(): item = QListWidgetItem(poly_lyr.name) item.setData(Qt.UserRole, poly_lyr) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(check) self.ui.IDC_listPolygons.addItem(item) def drawLine(self): if self.ui.IDC_rbDigi.isChecked() is False: self.ui.IDC_rbDigi.setChecked(True) self.dblckltemp = None self.rubberband.reset(self.polygon) self.__cleanDigi() self.__activateDigiTool() self.canvas.setMapTool(self.tool) def __createDigiFeature(self, pnts): u = Util(self.iface) f = u.createQgLineFeature(pnts) self.settings.mapData.customLine = f def __lineFinished(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) self.pointsToDraw.append(newPoint) #launch analyses self.iface.mainWindow().statusBar().showMessage(str(self.pointsToDraw)) if len(self.pointsToDraw) < 2: self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint self.drawnLine = None QMessageBox.warning(self, "VoGIS-Profiltool", QApplication.translate('code', 'Profillinie digitalisieren abgebrochen!', None, QApplication.UnicodeUTF8)) self.drawnLine = self.__createDigiFeature(self.pointsToDraw) self.__cleanDigi() self.pointsToDraw = [] self.dblclktemp = newPoint def __cleanDigi(self): self.pointsToDraw = [] self.canvas.unsetMapTool(self.tool) self.canvas.setMapTool(self.savedTool) def __activateDigiTool(self): QObject.connect(self.tool, SIGNAL("moved"), self.__moved) QObject.connect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.connect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.connect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) QObject.connect(self.tool, SIGNAL("deactivate"), self.__deactivateDigiTool) def __deactivateDigiTool(self): QObject.disconnect(self.tool, SIGNAL("moved"), self.__moved) QObject.disconnect(self.tool, SIGNAL("leftClicked"), self.__leftClicked) QObject.disconnect(self.tool, SIGNAL("rightClicked"), self.__rightClicked) QObject.disconnect(self.tool, SIGNAL("doubleClicked"), self.__doubleClicked) if QGis.QGIS_VERSION_INT < 10900: self.iface.mainWindow().statusBar().showMessage(QString("")) else: self.iface.mainWindow().statusBar().showMessage('') def __moved(self, position): if len(self.pointsToDraw) > 0: mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) self.rubberband.reset(self.polygon) newPnt = QgsPoint(mapPos.x(), mapPos.y()) if QGis.QGIS_VERSION_INT < 10900: for i in range(0, len(self.pointsToDraw)): self.rubberband.addPoint(self.pointsToDraw[i]) self.rubberband.addPoint(newPnt) else: pnts = self.pointsToDraw + [newPnt] self.rubberband.setToGeometry(QgsGeometry.fromPolyline(pnts),None) def __rightClicked(self, position): self.__lineFinished(position) def __leftClicked(self, position): mapPos = self.canvas.getCoordinateTransform().toMapCoordinates(position["x"], position["y"]) newPoint = QgsPoint(mapPos.x(), mapPos.y()) #if self.selectionmethod == 0: if newPoint == self.dblclktemp: self.dblclktemp = None return else: if len(self.pointsToDraw) == 0: self.rubberband.reset(self.polygon) self.pointsToDraw.append(newPoint) def __doubleClicked(self, position): pass #not in use right now def __lineCancel(self): pass def __getSettingsFromGui(self): self.settings.linesExplode = (self.ui.IDC_chkLinesExplode.checkState() == Qt.Checked) self.settings.linesMerge = (self.ui.IDC_chkLinesMerge.checkState() == Qt.Checked) self.settings.onlySelectedFeatures = (self.ui.IDC_chkOnlySelectedFeatures.checkState() == Qt.Checked) self.settings.equiDistance = self.ui.IDC_dblspinDistance.value() self.settings.vertexCnt = self.ui.IDC_dblspinVertexCnt.value() #self.settings.createHekto = (self.ui.IDC_chkCreateHekto.checkState() == Qt.Checked) self.settings.nodesAndVertices = (self.ui.IDC_chkNodesAndVertices.checkState() == Qt.Checked) if QGis.QGIS_VERSION_INT < 10900: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData( self.ui.IDC_cbLineLayers.currentIndex() ).toPyObject() ) else: self.settings.mapData.selectedLineLyr = (self.ui.IDC_cbLineLayers.itemData(self.ui.IDC_cbLineLayers.currentIndex())) if self.settings.onlySelectedFeatures is True and self.settings.mapData.selectedLineLyr.line.selectedFeatureCount() < 1: QMessageBox.warning(self.iface.mainWindow(), "VoGIS-Profiltool", QApplication.translate('code', u'Der gewählte Layer hat keine selektierten Elemente.', None, QApplication.UnicodeUTF8)) return False if self.ui.IDC_rbDigi.isChecked(): self.settings.modeLine = enumModeLine.customLine elif self.ui.IDC_rbShapeLine.isChecked(): self.settings.modeLine = enumModeLine.line else: #self.ui.IDC_rbStraigthLine self.settings.modeLine = enumModeLine.straightLine if self.ui.IDC_rbEquiDistance.isChecked(): self.settings.modeVertices = enumModeVertices.equiDistant else: self.settings.modeVertices = enumModeVertices.vertexCnt if self.ui.IDC_rbStraigthLine.isChecked(): ut = Util(self.iface) if ut.isFloat(self.ui.IDC_tbFromX.text(), QApplication.translate('code', 'Rechtswert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromX = float(self.ui.IDC_tbFromX.text()) if ut.isFloat(self.ui.IDC_tbFromY.text(), QApplication.translate('code', 'Hochwert von', None, QApplication.UnicodeUTF8)) is False: return False else: fromY = float(self.ui.IDC_tbFromY.text()) if ut.isFloat(self.ui.IDC_tbToX.text(), QApplication.translate('code', 'Rechtswert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toX = float(self.ui.IDC_tbToX.text()) if ut.isFloat(self.ui.IDC_tbToY.text(), QApplication.translate('code', 'Hochwert nach', None, QApplication.UnicodeUTF8)) is False: return False else: toY = float(self.ui.IDC_tbToY.text()) fromPnt = QgsPoint(fromX, fromY) toPnt = QgsPoint(toX, toY) self.settings.mapData.customLine = ut.createQgLineFeature([fromPnt, toPnt]) return True
class PlanetMainFilters(MAIN_FILTERS_BASE, MAIN_FILTERS_WIDGET, PlanetFilterMixin): leAOI: QLineEdit filtersChanged = pyqtSignal() zoomToAOIRequested = pyqtSignal() def __init__(self, iface, parent=None, plugin=None): super().__init__(parent=parent) self._iface: QgisInterface = iface self._plugin = plugin self.setupUi(self) self._aoi_box = QgsRubberBand(self._iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self._aoi_box.setFillColor(QColor(0, 0, 0, 0)) self._aoi_box.setStrokeColor(MAIN_AOI_COLOR) self._aoi_box.setWidth(3) self._aoi_box.setLineStyle(Qt.DashLine) self._canvas: QgsMapCanvas = self._iface.mapCanvas() # This may later be a nullptr, if no active tool when queried self._cur_maptool = None # self._json_exporter = QgsJsonExporter() # self._json_exporter.setIncludeAttributes(False) # noinspection PyUnresolvedReferences self.leAOI.textChanged['QString'].connect(self.filters_changed) # noinspection PyUnresolvedReferences self.leAOI.textEdited['QString'].connect(self.validate_edited_aoi) self._setup_tool_buttons() # Extent line edit tools self.btnZoomToAOI.clicked.connect(self.zoom_to_aoi) self.btnCopyAOI.clicked.connect(self.copy_aoi_to_clipboard) self.btnLoadAOI.clicked.connect(self.load_aoi_from_file) def reset_aoi_box(self): if self._aoi_box: self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) def filters(self): # return and_filter(geom_filter(self.leAOI.text), # date_range('acquired', gte=dateEditStart.text, # lte=dateEditEnd.text)) filters = [] if self.leAOI.text(): # TODO: Validate GeoJSON; try planet.api.utils.probably_geojson() # noinspection PyBroadException try: if qgsgeometry_from_geojson(self.leAOI.text()): aoi = json.loads(self.leAOI.text()) filters.append(geom_filter(aoi)) else: self._show_message("AOI not valid GeoJSON polygon", level=Qgis.Warning, duration=10) except: self._show_message("AOI not valid JSON", level=Qgis.Warning, duration=10) finally: return filters def filters_as_json(self): filters = [] if self.leAOI.text(): filters.append(self.leAOI.text()) return filters def load_filters(self, filter_json): pass def set_from_request(self, request): filters = self._filters_from_request(request, "geometry") if filters: geom = filters[0]["config"] txt = json.dumps(geom) self.leAOI.setText(txt) @pyqtSlot('QString') def filters_changed(self, value): # noinspection PyUnresolvedReferences self.filtersChanged.emit() @pyqtSlot() def clean_up(self): self.reset_aoi_box() def _setup_tool_buttons(self): extent_menu = QMenu(self) canvas_act = QAction('Current visible extent', extent_menu) # noinspection PyUnresolvedReferences canvas_act.triggered[bool].connect(self.aoi_from_current_extent) extent_menu.addAction(canvas_act) active_act = QAction('Active map layer extent', extent_menu) # noinspection PyUnresolvedReferences active_act.triggered[bool].connect(self.aoi_from_active_layer_extent) extent_menu.addAction(active_act) full_act = QAction('All map layers extent', extent_menu) # noinspection PyUnresolvedReferences full_act.triggered[bool].connect(self.aoi_from_full_extent) extent_menu.addAction(full_act) self.btnExtent.setMenu(extent_menu) # Also show menu on click, to keep disclosure triangle visible self.btnExtent.clicked.connect(self.btnExtent.showMenu) draw_menu = QMenu(self) box_act = QAction('Rectangle', draw_menu) # noinspection PyUnresolvedReferences box_act.triggered[bool].connect(self.aoi_from_box) draw_menu.addAction(box_act) circle_act = QAction('Circle', draw_menu) # noinspection PyUnresolvedReferences circle_act.triggered[bool].connect(self.aoi_from_circle) draw_menu.addAction(circle_act) polygon_act = QAction('Polygon', draw_menu) # noinspection PyUnresolvedReferences polygon_act.triggered[bool].connect(self.aoi_from_polygon) draw_menu.addAction(polygon_act) self.btnDraw.setMenu(draw_menu) # Also show menu on click, to keep disclosure triangle visible self.btnDraw.clicked.connect(self.btnDraw.showMenu) selection_menu = QMenu(self) self.single_select_act = QAction('Single feature', selection_menu) # noinspection PyUnresolvedReferences self.single_select_act.triggered[bool].connect(self.aoi_from_feature) selection_menu.addAction(self.single_select_act) self.bound_select_act = QAction('Multiple features (bounding box)', selection_menu) # noinspection PyUnresolvedReferences self.bound_select_act.triggered[bool].connect(self.aoi_from_bound) selection_menu.addAction(self.bound_select_act) self.btnSelection.setMenu(selection_menu) # Also show menu on click, to keep disclosure triangle visible self.btnSelection.clicked.connect(self._toggle_selection_tools) self.btnSelection.clicked.connect(self.btnSelection.showMenu) def _toggle_selection_tools(self): active_layer = self._iface.activeLayer() is_vector = isinstance(active_layer, QgsVectorLayer) if is_vector and active_layer.selectedFeatureCount(): if active_layer.selectedFeatureCount() > 1: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(True) elif active_layer.selectedFeatureCount(): self.single_select_act.setEnabled(True) self.bound_select_act.setEnabled(False) else: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(False) else: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(False) @pyqtSlot() # noinspection PyArgumentList def aoi_from_current_extent(self): """Return current map extent as geojson transformed to EPSG:4326 """ if not self._iface: log.debug('No iface object, skipping AOI extent') return canvas = self._iface.mapCanvas() # noinspection PyArgumentList transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) canvas_extent: QgsRectangle = canvas.extent() transform_extent = transform.transformBoundingBox(canvas_extent) # noinspection PyArgumentList geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson() # noinspection PyArgumentList self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas.extent())) self.leAOI.setText(extent_json) log.debug('AOI set to canvas extent') self.zoom_to_aoi() @pyqtSlot() # noinspection PyArgumentList def aoi_from_active_layer_extent(self): """Return active map layer extent as geojson transformed to EPSG:4326 """ if not self._iface: log.debug('No iface object, skipping AOI extent') return map_layer: QgsMapLayer = self._iface.activeLayer() if map_layer is None: log.debug('No active layer selected, skipping AOI extent') return if not map_layer.isValid(): log.debug('Active map layer invalid, skipping AOI extent') return # noinspection PyArgumentList transform = QgsCoordinateTransform( map_layer.crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) ml_extent: QgsRectangle = map_layer.extent() transform_extent = transform.transformBoundingBox(ml_extent) # noinspection PyArgumentList geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson() # noinspection PyArgumentList,PyCallByClass self._aoi_box.setToGeometry(QgsGeometry.fromRect(ml_extent)) self.leAOI.setText(extent_json) log.debug('AOI set to active layer extent') self.zoom_to_aoi() @pyqtSlot() # noinspection PyArgumentList def aoi_from_full_extent(self): """Return full data map extent as geojson transformed to EPSG:4326 """ if not self._iface: log.debug('No iface object, skipping AOI extent') return canvas = self._iface.mapCanvas() # noinspection PyArgumentList transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) canvas_extent: QgsRectangle = canvas.fullExtent() transform_extent = transform.transformBoundingBox(canvas_extent) # noinspection PyArgumentList geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson() # noinspection PyArgumentList,PyCallByClass self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas_extent)) self.leAOI.setText(extent_json) log.debug('AOI set to full data extent') self.zoom_to_aoi() @pyqtSlot() def aoi_from_box(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetExtentMapTool(self._iface.mapCanvas()) self._iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.extentSelected.connect(self.set_draw_aoi) @pyqtSlot() def aoi_from_circle(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetCircleMapTool(self._iface.mapCanvas()) self._iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.circleSelected.connect(self.set_draw_aoi) @pyqtSlot() def aoi_from_polygon(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetPolyMapTool(self._iface.mapCanvas()) self._iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.polygonSelected.connect(self.set_draw_aoi) @pyqtSlot(object) def set_draw_aoi(self, aoi): # noinspection PyArgumentList transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) aoi_json = None if isinstance(aoi, QgsRectangle): aoi_geom = QgsGeometry().fromRect(aoi) self._aoi_box.setToGeometry(aoi_geom) aoi_geom.transform(transform) aoi_json = aoi_geom.asJson() if isinstance(aoi, QgsGeometry): self._aoi_box.setToGeometry(aoi) # TODO: validate geom is less than 500 vertices aoi.transform(transform) aoi_json = aoi.asJson() if aoi_json: self.leAOI.setText(aoi_json) # noinspection PyUnresolvedReferences self._show_message('AOI set to drawn figure') self.zoom_to_aoi() if self._cur_maptool is not None: # Restore previously used maptool self._canvas.setMapTool(self._cur_maptool) self._cur_maptool = None else: # Fallback to activating pan tool self._iface.actionPan().trigger() else: # noinspection PyUnresolvedReferences self._show_message('AOI unable to be set', level=Qgis.Warning, duration=10) @pyqtSlot() def aoi_from_feature(self): layer: QgsVectorLayer = self._iface.activeLayer() if layer.selectedFeatureCount() > 1: self._show_message('More than 1 feature. Searching by bbox.', level=Qgis.Warning, duration=10) self.aoi_from_bound() return elif layer.selectedFeatureCount() < 1: self._show_message('No features selected.', level=Qgis.Warning, duration=10) return selected: QgsFeature = layer.selectedFeatures()[0] if selected.geometry().isMultipart(): multi_geom = selected.geometry().asGeometryCollection() if len(multi_geom) > 1: self._show_message('More than 1 geometry. Searching by bbox.', level=Qgis.Warning, duration=10) self.aoi_from_bound() return elif len(multi_geom) < 1: self._show_message('No geometry selected.', level=Qgis.Warning, duration=10) return else: geom: QgsGeometry = multi_geom[0] else: geom: QgsGeometry = selected.geometry() if geom.constGet().vertexCount() > 500: self._show_message('More than 500 vertices. Searching by bbox.', level=Qgis.Warning, duration=10) self.aoi_from_bound() return # noinspection PyArgumentList trans_layer = QgsCoordinateTransform( layer.sourceCrs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) # noinspection PyArgumentList trans_canvas = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance().crs(), QgsProject.instance()) # geom.transform(transform) geom.transform(trans_layer) geom_json = geom.asJson() self.leAOI.setText(geom_json) geom.transform(trans_canvas) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.zoom_to_aoi() @pyqtSlot() def aoi_from_bound(self): layer: QgsVectorLayer = self._iface.activeLayer() if layer.selectedFeatureCount() < 1: self._show_message('No features selected.', level=Qgis.Warning, duration=10) return bbox = layer.boundingBoxOfSelected() # noinspection PyArgumentList trans_layer = QgsCoordinateTransform( layer.sourceCrs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance()) # noinspection PyArgumentList trans_canvas = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance().crs(), QgsProject.instance()) transform_bbox = trans_layer.transformBoundingBox(bbox) # noinspection PyArgumentList geom_bbox = QgsGeometry.fromRect(transform_bbox) bbox_json = geom_bbox.asJson() self.leAOI.setText(bbox_json) bbox_canvas = trans_canvas.transformBoundingBox(transform_bbox) # noinspection PyArgumentList self._aoi_box.setToGeometry(QgsGeometry.fromRect(bbox_canvas)) self.zoom_to_aoi() def hide_aoi_if_matches_geom(self, geom): color = (QColor(0, 0, 0, 0) if self._aoi_box.asGeometry().equals(geom) else MAIN_AOI_COLOR) self._aoi_box.setStrokeColor(color) def show_aoi(self): if self._aoi_box is not None: self._aoi_box.setStrokeColor(MAIN_AOI_COLOR) def aoi_geom(self): if self._aoi_box is not None: return self._aoi_box.asGeometry() @pyqtSlot() def zoom_to_aoi(self): if not self._iface: log.debug('No iface object, skipping AOI extent') return if not self.leAOI.text(): log.debug('No AOI defined, skipping zoom to AOI') return geom: QgsGeometry = qgsgeometry_from_geojson(self.leAOI.text()) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.show_aoi() zoom_canvas_to_aoi(self.leAOI.text(), iface_obj=self._iface) self.zoomToAOIRequested.emit() @pyqtSlot() def copy_aoi_to_clipboard(self): if not self.leAOI.text(): log.debug('No AOI defined, skipping zoom to AOI') return json_geom_txt = json.dumps(json.loads(self.leAOI.text()), indent=2) cb = QgsApplication.clipboard() cb.setText(json_geom_txt) # noinspection PyUnresolvedReferences self._show_message('AOI copied to clipboard') @pyqtSlot() def load_aoi_from_file(self): path, _ = QFileDialog.getOpenFileName(self, "Open GeoJSON AOI file", QDir.homePath(), "JSON (*.json);;All Files (*)") file = QFile(path) if not file.open(QFile.ReadOnly | QFile.Text): return inf = QTextStream(file) json_txt = inf.readAll() try: json_obj = json.loads(json_txt) except ValueError: # noinspection PyUnresolvedReferences self._show_message('GeoJSON from file invalid', level=Qgis.Warning, duration=10) return json_geom = geometry_from_json(json_obj) if not json_geom: # noinspection PyUnresolvedReferences self._show_message('GeoJSON geometry from file invalid', level=Qgis.Warning, duration=10) return geom: QgsGeometry = qgsgeometry_from_geojson(json_geom) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.leAOI.setText(json.dumps(json_geom)) self.zoom_to_aoi() @pyqtSlot() def validate_aoi(self): # TODO:gather existing validation logic here # TODO: Check for valid json.loads # TODO: Check API verticie limit of 500 pass @pyqtSlot() def validate_edited_aoi(self): json_txt = self.leAOI.text() if not json_txt: self.reset_aoi_box() log.debug('No AOI defined, skipping validation') return try: json_obj = json.loads(json_txt) except ValueError: # noinspection PyUnresolvedReferences self._show_message('AOI GeoJSON is invalid', level=Qgis.Warning, duration=10) return json_geom = geometry_from_json(json_obj) if not json_geom: # noinspection PyUnresolvedReferences self._show_message('AOI GeoJSON geometry invalid', level=Qgis.Warning, duration=10) return geom: QgsGeometry = qgsgeometry_from_geojson(json_geom) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.leAOI.blockSignals(True) self.leAOI.setText(json.dumps(json_geom)) self.leAOI.blockSignals(False) self.zoom_to_aoi()
class PlanetAOIFilter(AOI_FILTER_BASE, AOI_FILTER_WIDGET, PlanetFilterMixin): filtersChanged = pyqtSignal() savedSearchSelected = pyqtSignal(object) zoomToAOIRequested = pyqtSignal() def __init__( self, parent=None, plugin=None, color=MAIN_AOI_COLOR, ): super().__init__(parent=parent) self._plugin = plugin self.setupUi(self) self.emitFiltersChanged = False self.color = color self._aoi_box = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self._aoi_box.setFillColor(QColor(0, 0, 0, 0)) self._aoi_box.setStrokeColor(color) self._aoi_box.setWidth(3) self._aoi_box.setLineStyle(Qt.DashLine) self._canvas: QgsMapCanvas = iface.mapCanvas() # This may later be a nullptr, if no active tool when queried self._cur_maptool = None self.leAOI.textChanged["QString"].connect(self.filters_changed) self.leAOI.textEdited["QString"].connect(self.validate_edited_aoi) self._setup_tool_buttons() # Extent line edit tools self.btnZoomToAOI.clicked.connect(self.zoom_to_aoi) self.btnCopyAOI.clicked.connect(self.copy_aoi_to_clipboard) self.p_client = PlanetClient.getInstance() def reset_aoi_box(self): self.leAOI.setText("") if self._aoi_box: self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) def filters(self): filters = [] if self.leAOI.text(): try: qgsgeom = qgsgeometry_from_geojson(self.leAOI.text()) if not qgsgeom.isEmpty(): geom_json = json.loads(qgsgeom.asJson()) filters.append(geom_filter(geom_json)) else: self._show_message("AOI not valid GeoJSON polygon", level=Qgis.Warning, duration=10) except Exception: self._show_message("AOI not valid JSON", level=Qgis.Warning, duration=10) finally: return filters def filters_as_json(self): filters = [] if self.leAOI.text(): filters.append(self.leAOI.text()) return filters def set_from_request(self, request): self.emitFiltersChanged = False filters = filters_from_request(request, "geometry") if filters: geom = filters[0]["config"] txt = json.dumps(geom) self.leAOI.setText(txt) else: self.leAOI.setText("") self.emitFiltersChanged = True @pyqtSlot("QString") def filters_changed(self, value): if self.emitFiltersChanged: self.filtersChanged.emit() @pyqtSlot() def clean_up(self): self.reset_aoi_box() def _setup_tool_buttons(self): extent_menu = QMenu(self) canvas_act = QAction("Current visible extent", extent_menu) canvas_act.triggered[bool].connect(self.aoi_from_current_extent) extent_menu.addAction(canvas_act) active_act = QAction("Active map layer extent", extent_menu) active_act.triggered[bool].connect(self.aoi_from_active_layer_extent) extent_menu.addAction(active_act) full_act = QAction("All map layers extent", extent_menu) full_act.triggered[bool].connect(self.aoi_from_full_extent) extent_menu.addAction(full_act) self.btnExtent.setMenu(extent_menu) # Also show menu on click, to keep disclosure triangle visible self.btnExtent.clicked.connect(self.btnExtent.showMenu) draw_menu = QMenu(self) box_act = QAction("Rectangle", draw_menu) box_act.triggered[bool].connect(self.aoi_from_box) draw_menu.addAction(box_act) circle_act = QAction("Circle", draw_menu) circle_act.triggered[bool].connect(self.aoi_from_circle) draw_menu.addAction(circle_act) polygon_act = QAction("Polygon", draw_menu) polygon_act.triggered[bool].connect(self.aoi_from_polygon) draw_menu.addAction(polygon_act) self.btnDraw.setMenu(draw_menu) # Also show menu on click, to keep disclosure triangle visible self.btnDraw.clicked.connect(self.btnDraw.showMenu) selection_menu = QMenu(self) self.single_select_act = QAction("Single feature", selection_menu) self.single_select_act.triggered[bool].connect(self.aoi_from_feature) selection_menu.addAction(self.single_select_act) self.bound_select_act = QAction("Multiple features (bounding box)", selection_menu) self.bound_select_act.triggered[bool].connect(self.aoi_from_bound) selection_menu.addAction(self.bound_select_act) self.btnSelection.setMenu(selection_menu) # Also show menu on click, to keep disclosure triangle visible self.btnSelection.clicked.connect(self._toggle_selection_tools) self.btnSelection.clicked.connect(self.btnSelection.showMenu) upload_menu = QMenu(self) upload_act = QAction("Upload vector layer file", upload_menu) upload_act.triggered[bool].connect(self.upload_file) upload_menu.addAction(upload_act) self.btnUpload.setMenu(upload_menu) self.btnUpload.clicked.connect(self.btnUpload.showMenu) def upload_file(self): filename, _ = QFileDialog.getOpenFileName(self, "Select AOI file", "", "All files(*.*)") if filename: layer = QgsVectorLayer(filename, "") self.aoi_from_layer(layer) def aoi_from_layer(self, layer): if not layer.isValid(): self._show_message("Invalid layer", level=Qgis.Warning, duration=10) else: feature = next(layer.getFeatures(), None) if feature is None: self._show_message("Layer contains no features", level=Qgis.Warning, duration=10) else: geom = feature.geometry() transform = QgsCoordinateTransform( layer.crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) try: geom.transform(transform) except QgsCsException: self._show_message( "Could not convert AOI to EPSG:4326", level=Qgis.Warning, duration=10, ) return geom_json = geom.asJson(precision=6) self._aoi_box.setToGeometry(geom) self.leAOI.setText(geom_json) log.debug("AOI set to layer") self.zoom_to_aoi() def _toggle_selection_tools(self): active_layer = iface.activeLayer() is_vector = isinstance(active_layer, QgsVectorLayer) if is_vector and active_layer.selectedFeatureCount(): if active_layer.selectedFeatureCount() > 1: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(True) elif active_layer.selectedFeatureCount(): self.single_select_act.setEnabled(True) self.bound_select_act.setEnabled(False) else: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(False) else: self.single_select_act.setEnabled(False) self.bound_select_act.setEnabled(False) @pyqtSlot() def aoi_from_current_extent(self): """Return current map extent as geojson transformed to EPSG:4326""" canvas = iface.mapCanvas() transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) canvas_extent: QgsRectangle = canvas.extent() try: transform_extent = transform.transformBoundingBox(canvas_extent) except QgsCsException: self._show_message("Could not convert AOI to EPSG:4326", level=Qgis.Warning, duration=10) return geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson(precision=6) self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas.extent())) self.leAOI.setText(extent_json) log.debug("AOI set to canvas extent") self.zoom_to_aoi() @pyqtSlot() def aoi_from_active_layer_extent(self): """Return active map layer extent as geojson transformed to EPSG:4326""" map_layer: QgsMapLayer = iface.activeLayer() if map_layer is None: log.debug("No active layer selected, skipping AOI extent") return if not map_layer.isValid(): log.debug("Active map layer invalid, skipping AOI extent") return transform = QgsCoordinateTransform( map_layer.crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) ml_extent: QgsRectangle = map_layer.extent() try: transform_extent = transform.transformBoundingBox(ml_extent) except QgsCsException: self._show_message("Could not convert AOI to EPSG:4326", level=Qgis.Warning, duration=10) return geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson(precision=6) self.leAOI.setText(extent_json) log.debug("AOI set to active layer extent") self.zoom_to_aoi() @pyqtSlot() def aoi_from_full_extent(self): """Return full data map extent as geojson transformed to EPSG:4326""" canvas = iface.mapCanvas() transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) canvas_extent: QgsRectangle = canvas.fullExtent() if canvas_extent.isNull(): # Canvas not yet initialized return try: transform_extent = transform.transformBoundingBox(canvas_extent) except QgsCsException: self._show_message("Could not convert AOI to EPSG:4326", level=Qgis.Warning, duration=10) return geom_extent = QgsGeometry.fromRect(transform_extent) extent_json = geom_extent.asJson(precision=6) self._aoi_box.setToGeometry(QgsGeometry.fromRect(canvas_extent)) self.leAOI.setText(extent_json) log.debug("AOI set to full data extent") self.zoom_to_aoi() @pyqtSlot() def aoi_from_box(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetExtentMapTool(iface.mapCanvas()) iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.extentSelected.connect(self.set_draw_aoi) @pyqtSlot() def aoi_from_circle(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetCircleMapTool(iface.mapCanvas()) iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.circleSelected.connect(self.set_draw_aoi) @pyqtSlot() def aoi_from_polygon(self): self._cur_maptool: QgsMapTool = self._canvas.mapTool() self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) aoi_draw = PlanetPolyMapTool(iface.mapCanvas()) iface.mapCanvas().setMapTool(aoi_draw) aoi_draw.polygonSelected.connect(self.set_draw_aoi) @pyqtSlot(object) def set_draw_aoi(self, aoi): transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) aoi_json = None if isinstance(aoi, QgsRectangle): aoi_geom = QgsGeometry().fromRect(aoi) self._aoi_box.setToGeometry(aoi_geom) aoi_geom.transform(transform) aoi_json = aoi_geom.asJson(precision=6) if isinstance(aoi, QgsGeometry): self._aoi_box.setToGeometry(aoi) # TODO: validate geom is less than 500 vertices aoi.transform(transform) aoi_json = aoi.asJson(precision=6) if aoi_json: self.leAOI.setText(aoi_json) self._show_message("AOI set to drawn figure") self.zoom_to_aoi() if self._cur_maptool is not None: # Restore previously used maptool self._canvas.setMapTool(self._cur_maptool) self._cur_maptool = None else: # Fallback to activating pan tool iface.actionPan().trigger() else: self._show_message("AOI unable to be set", level=Qgis.Warning, duration=10) @pyqtSlot() def aoi_from_feature(self): layer = iface.activeLayer() if not isinstance(layer, QgsVectorLayer): self._show_message("Active layer must be a vector layer.", level=Qgis.Warning, duration=10) return if layer.selectedFeatureCount() > 1: self._show_message( "More than 1 feature. Searching by bbox.", level=Qgis.Warning, duration=10, ) self.aoi_from_bound() return elif layer.selectedFeatureCount() < 1: self._show_message("No features selected.", level=Qgis.Warning, duration=10) return selected: QgsFeature = layer.selectedFeatures()[0] geom: QgsGeometry = selected.geometry() if geom.constGet().vertexCount() > 500: self._show_message( "More than 500 vertices. Searching by bbox.", level=Qgis.Warning, duration=10, ) self.aoi_from_bound() return trans_layer = QgsCoordinateTransform( layer.sourceCrs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) trans_canvas = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance().crs(), QgsProject.instance(), ) # geom.transform(transform) geom.transform(trans_layer) geom_json = geom.asJson(precision=6) self.leAOI.setText(geom_json) geom.transform(trans_canvas) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.zoom_to_aoi() @pyqtSlot() def aoi_from_bound(self): layer = iface.activeLayer() if not isinstance(layer, QgsVectorLayer): self._show_message("Active layer must be a vector layer.", level=Qgis.Warning, duration=10) return if layer.selectedFeatureCount() < 1: self._show_message("No features selected.", level=Qgis.Warning, duration=10) return bbox = layer.boundingBoxOfSelected() trans_layer = QgsCoordinateTransform( layer.sourceCrs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) trans_canvas = QgsCoordinateTransform( QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance().crs(), QgsProject.instance(), ) transform_bbox = trans_layer.transformBoundingBox(bbox) geom_bbox = QgsGeometry.fromRect(transform_bbox) bbox_json = geom_bbox.asJson(precision=6) self.leAOI.setText(bbox_json) bbox_canvas = trans_canvas.transformBoundingBox(transform_bbox) self._aoi_box.setToGeometry(QgsGeometry.fromRect(bbox_canvas)) self.zoom_to_aoi() def hide_aoi_if_matches_geom(self, geom): color = (QColor(0, 0, 0, 0) if self._aoi_box.asGeometry().equals(geom) else self.color) self._aoi_box.setStrokeColor(color) def show_aoi(self): if self._aoi_box is not None: self._aoi_box.setStrokeColor(self.color) def aoi_geom(self): if self._aoi_box is not None: return self._aoi_box.asGeometry() def aoi_as_4326_geom(self): transform = QgsCoordinateTransform( QgsProject.instance().crs(), QgsCoordinateReferenceSystem("EPSG:4326"), QgsProject.instance(), ) geom = self.aoi_geom() if geom is not None: geom.transform(transform) return geom @pyqtSlot() def zoom_to_aoi(self): if not self.leAOI.text(): log.debug("No AOI defined, skipping zoom to AOI") return geom: QgsGeometry = qgsgeometry_from_geojson(self.leAOI.text()) if geom.isEmpty(): self._show_message("AOI GeoJSON geometry invalid", level=Qgis.Warning, duration=10) return self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.show_aoi() zoom_canvas_to_aoi(self.leAOI.text()) self.zoomToAOIRequested.emit() @pyqtSlot() def copy_aoi_to_clipboard(self): if not self.leAOI.text(): log.debug("No AOI defined, skipping zoom to AOI") return try: json_obj = json.loads(self.leAOI.text()) except ValueError: return json_geom_txt = json.dumps(json_obj, indent=2) cb = QgsApplication.clipboard() cb.setText(json_geom_txt) self._show_message("AOI copied to clipboard") @pyqtSlot() def validate_edited_aoi(self): json_txt = self.leAOI.text() if not json_txt: self.reset_aoi_box() log.debug("No AOI defined, skipping validation") return try: json_obj = json.loads(json_txt) except ValueError: self._show_message("AOI GeoJSON is invalid", level=Qgis.Warning, duration=10) return try: json_geom = geometry_from_json(json_obj) except Exception: json_geom = None if not json_geom: self._show_message("AOI GeoJSON geometry invalid", level=Qgis.Warning, duration=10) return geom: QgsGeometry = qgsgeometry_from_geojson(json_geom) self._aoi_box.setToGeometry(geom, QgsCoordinateReferenceSystem("EPSG:4326")) self.leAOI.blockSignals(True) self.leAOI.setText(json.dumps(json_geom)) self.leAOI.blockSignals(False) self.zoom_to_aoi()
class MoveTool(QgsMapToolAdvancedDigitizing): """ Map tool class to move or copy an object """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/move_icon.png' self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature") self.setCursor(Qt.ArrowCursor) self.__isEditing = False self.__findVertex = False self.__onMove = False self.__layer = None self.__confDlg = None self.__lastFeatureId = None self.__selectedFeature = None self.__rubberBand = None self.__rubberSnap = None self.__newFeature = None self.__selectedVertex = None def activate(self): """ When the action is selected """ QgsMapToolAdvancedDigitizing.activate(self) if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def deactivate(self): """ When the action is deselected """ self.__cancel() QgsMapToolAdvancedDigitizing.deactivate(self) def toolName(self): """ To get the tool name :return: tool name """ return QCoreApplication.translate("VDLTools", "Move/Copy") def startEditing(self): """ To set the action as enable, as the layer is editable """ self.action().setEnabled(True) Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer.editingStopped.connect(self.stopEditing) def stopEditing(self): """ To set the action as disable, as the layer is not editable """ self.action().setEnabled(False) Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def __cancel(self): """ To cancel used variables """ if self.__rubberBand is not None: self.canvas().scene().removeItem(self.__rubberBand) self.__rubberBand.reset() self.__rubberBand = None if self.__rubberSnap is not None: self.canvas().scene().removeItem(self.__rubberSnap) self.__rubberSnap.reset() self.__rubberSnap = None self.__isEditing = False self.__findVertex = False self.__onMove = False self.__lastFeatureId = None self.__selectedFeature = None self.__confDlg = None self.__newFeature = None self.__selectedVertex = None self.__layer.removeSelection() if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) def __removeLayer(self): """ To remove the current working layer """ if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = None def setEnable(self, layer): """ To check if we can enable the action for the selected layer :param layer: selected layer """ if layer is not None and layer.type() == QgsMapLayer.VectorLayer: if layer == self.__layer: return if self.__layer is not None: if self.__layer.isEditable(): Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing) else: Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing) self.__layer = layer if self.__layer.geometryType() == QGis.Point: self.setMode(self.CaptureLine) else: self.setMode(self.CaptureNone) if self.__layer.isEditable(): self.action().setEnabled(True) self.__layer.editingStopped.connect(self.stopEditing) else: self.action().setEnabled(False) self.__layer.editingStarted.connect(self.startEditing) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() return self.action().setEnabled(False) if self.canvas().mapTool() == self: self.__iface.actionPan().trigger() self.__removeLayer() def __pointPreview(self, point): """ To create a point geometry preview (rubberBand) :param point: new position as mapPoint """ point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface) self.__newFeature = QgsPointV2(point.x(), point.y()) self.__newFeature.addZValue(point_v2.z()) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None) def __linePreview(self, point): """ To create a line geometry preview (rubberBand) :param point: new position as mapPoint """ line_v2, curved = GeometryV2.asLineV2( self.__selectedFeature.geometry(), self.__iface) vertex = QgsPointV2() line_v2.pointAt(self.__selectedVertex, vertex) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) dx = vertex.x() - point.x() dy = vertex.y() - point.y() if isinstance(curved, (list, tuple)): self.__newFeature = QgsCompoundCurveV2() for pos in range(line_v2.nCurves()): curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy) self.__newFeature.addCurve(curve_v2) if pos == 0: self.__rubberBand.setToGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__rubberBand.addGeometry( QgsGeometry(curve_v2.curveToLine()), None) else: self.__newFeature = self.__newCurve(curved, line_v2, dx, dy) self.__rubberBand.setToGeometry( QgsGeometry(self.__newFeature.curveToLine()), None) @staticmethod def __newCurve(curved, line_v2, dx, dy): """ To create a new moved line :param curved: if the line is curved :param line_v2: the original line :param dx: x translation :param dy: y translation :return: the new line """ if curved: newCurve = QgsCircularStringV2() else: newCurve = QgsLineStringV2() points = [] for pos in range(line_v2.numPoints()): x = line_v2.pointN(pos).x() - dx y = line_v2.pointN(pos).y() - dy pt = QgsPointV2(x, y) pt.addZValue(line_v2.pointN(pos).z()) points.append(pt) newCurve.setPoints(points) return newCurve def __polygonPreview(self, point): """ To create a polygon geometry preview (rubberBand) :param point: new position as mapPoint """ polygon_v2, curved = GeometryV2.asPolygonV2( self.__selectedFeature.geometry(), self.__iface) vertex = polygon_v2.vertexAt( GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex)) dx = vertex.x() - point.x() dy = vertex.y() - point.y() self.__newFeature = QgsCurvePolygonV2() self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line) line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy) self.__newFeature.setExteriorRing(line_v2) self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None) for num in range(polygon_v2.numInteriorRings()): line_v2 = self.__newCurve(curved[num + 1], polygon_v2.interiorRing(num), dx, dy) self.__newFeature.addInteriorRing(line_v2) self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None) def __onConfirmCancel(self): """ When the Cancel button in Move Confirm Dialog is pushed """ self.__confDlg.reject() def __onConfirmMove(self): """ When the Move button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage(QCoreApplication.translate( "VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) self.__layer.changeGeometry(self.__selectedFeature.id(), geometry) self.__confDlg.accept() self.__cancel() def __onConfirmCopy(self): """ When the Copy button in Move Confirm Dialog is pushed """ geometry = QgsGeometry(self.__newFeature) if not geometry.isGeosValid(): self.__iface.messageBar().pushMessage(QCoreApplication.translate( "VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0) feature = QgsFeature(self.__layer.pendingFields()) feature.setGeometry(geometry) primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn() for field in self.__selectedFeature.fields(): if field.name() != primaryKey: feature.setAttribute( field.name(), self.__selectedFeature.attribute(field.name())) if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \ QgsEditFormConfig.SuppressOn: self.__iface.openFeatureForm(self.__layer, feature) else: self.__layer.addFeature(feature) self.__confDlg.accept() self.__cancel() def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def cadCanvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if type(event) == QMoveEvent: map_point = self.toMapCoordinates(event.pos()) else: map_point = event.mapPoint() if not self.__isEditing and not self.__findVertex and not self.__onMove: laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10, QgsTolerance.Pixels) f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings]) if f_l is not None and self.__lastFeatureId != f_l[0].id(): self.__lastFeatureId = f_l[0].id() self.__layer.setSelectedFeatures([f_l[0].id()]) if f_l is None: self.__layer.removeSelection() self.__lastFeatureId = None elif self.__findVertex: if self.__rubberBand is not None: self.__rubberBand.reset() closest = self.__selectedFeature.geometry().closestVertex( map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__rubberBand.setToGeometry( QgsGeometry().fromPoint(closest[0]), None) elif self.__onMove: if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(map_point) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(map_point) else: self.__pointPreview(map_point) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) self.__rubberBand.setWidth(2) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(8) if self.__rubberSnap is not None: self.__rubberSnap.reset() else: self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point) self.__rubberSnap.setColor(color) self.__rubberSnap.setWidth(2) self.__rubberSnap.setIconSize(20) match = Finder.snap(map_point, self.canvas()) if match.hasVertex() or match.hasEdge(): point = match.point() if match.hasVertex(): if match.layer(): self.__rubberSnap.setIcon(4) else: self.__rubberSnap.setIcon(1) if match.hasEdge(): intersection = Finder.snapCurvedIntersections( point, self.canvas(), self) if intersection is not None: self.__rubberSnap.setIcon(1) point = intersection else: self.__rubberSnap.setIcon(3) self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None) def cadCanvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if not self.__isEditing and not self.__findVertex and not self.__onMove: found_features = self.__layer.selectedFeatures() if len(found_features) > 0: if len(found_features) > 1: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO) return self.__selectedFeature = found_features[0] if self.__layer.geometryType() != QGis.Point: self.__iface.messageBar().pushMessage( QCoreApplication.translate( "VDLTools", "Select vertex for moving (ESC to undo)"), level=QgsMessageBar.INFO, duration=3) self.__findVertex = True self.setMode(self.CaptureLine) self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point) else: self.setMode(self.CaptureNone) self.__onMove = True elif self.__findVertex: self.__findVertex = False self.setMode(self.CaptureNone) closest = self.__selectedFeature.geometry().closestVertex( event.mapPoint()) self.__selectedVertex = closest[1] self.__onMove = True elif self.__onMove: self.__onMove = False mapPoint = event.mapPoint() match = Finder.snap(event.mapPoint(), self.canvas()) if match.hasVertex() or match.hasEdge(): mapPoint = match.point() if match.hasEdge(): intersection = Finder.snapCurvedIntersections( mapPoint, self.canvas(), self) if intersection is not None: mapPoint = intersection self.__isEditing = True if self.__rubberBand is not None: self.__rubberBand.reset() if self.__layer.geometryType() == QGis.Polygon: self.__polygonPreview(mapPoint) elif self.__layer.geometryType() == QGis.Line: self.__linePreview(mapPoint) else: self.__pointPreview(mapPoint) color = QColor("red") color.setAlphaF(0.78) self.__rubberBand.setColor(color) if self.__layer.geometryType() != QGis.Point: self.__rubberBand.setWidth(2) self.__rubberBand.setLineStyle(Qt.DotLine) else: self.__rubberBand.setIcon(4) self.__rubberBand.setIconSize(20) self.__confDlg = MoveConfirmDialog() self.__confDlg.rejected.connect(self.__cancel) self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove) self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy) self.__confDlg.cancelButton().clicked.connect( self.__onConfirmCancel) self.__confDlg.show()
class ApisMapToolEmitPointAndSquare(QgsMapTool, ApisMapToolMixin): # when mapping finished signal emitted that carries the Point and a Polygon Geometry (in Map Coordinates) mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas, diagonal=200): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.vertexMarker = None self.rubberBand = None self.capturedPoint = None self.derivedPolygon = [] self.capturing = False # self.setLayers(pointLayer, polygonLayer) self.setDiagonal(diagonal) self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.setVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getCapturedPoint() polygon = self.getDerivedPolygon() self.stopCapturing() if point != None and polygon != None: self.mappingFinished.emit(self.getPointGeometry(point), self.getPolygonGeometry(polygon), self.canvas.mapSettings().destinationCrs()) 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.getCapturedPoint() polygon = self.getDerivedPolygon() self.stopCapturing() if point != None and polygon != None: self.mappingFinished.emit(self.getPointGeometry(point), self.getPolygonGeometry(polygon), self.canvas.mapSettings().destinationCrs()) def startCapturing(self): self.clearScene() 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.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.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 def stopCapturing(self): self.capturing = False self.capturedPoint = None self.derivedPolygon = [] self.canvas.refresh() def setVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) # set/update vertexMarker Position self.vertexMarker.setCenter(mapPt) self.capturedPoint = mapPt # update rubberBand self.updateRubberBand() def updateRubberBand(self): if self.capturedPoint and self.rubberBand: # calculate Points self.derivedPolygon = self.calculateSquare(self.capturedPoint) self.rubberBand.reset(QGis.Polygon) for mapPt in self.derivedPolygon: self.rubberBand.addPoint(mapPt) def getCapturedPoint(self): point = self.capturedPoint if point == None: return None else: return point def getDerivedPolygon(self): polygon = self.derivedPolygon if polygon == None: return None else: return polygon def getPointGeometry(self, geom): return QgsGeometry.fromPoint(geom) def getPolygonGeometry(self, geom): return QgsGeometry.fromPolygon([geom]) def updateDiagonal(self, diagonal): self.setDiagonal(diagonal) # update Rubberband self.updateRubberBand()
class RectangleMapTool(QgsMapTool): def __init__(self, canvas, callback): self.canvas = canvas QgsMapTool.__init__(self, self.canvas) self.callback = callback self.rubberBand = QgsRubberBand(self.canvas, True) self.rubberBand.setColor(QColor(227, 26, 28, 255)) self.rubberBand.setWidth(5) self.rubberBand.setLineStyle(Qt.PenStyle(Qt.DashLine)) self.reset() def reset(self): self.startPoint = self.endPoint = None self.isEmittingPoint = False self.rubberBand.reset(True) def canvasPressEvent(self, e): self.startPoint = self.toMapCoordinates(e.pos()) self.endPoint = self.startPoint self.isEmittingPoint = True self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): self.isEmittingPoint = False r = self.rectangle() if r is not None: # print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() self.rubberBand.hide() self.callback(r) # self.deactivate() return None def canvasMoveEvent(self, e): if not self.isEmittingPoint: return self.endPoint = self.toMapCoordinates(e.pos()) self.showRect(self.startPoint, self.endPoint) def showRect(self, startPoint, endPoint): self.rubberBand.reset(True) if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y(): return point1 = QgsPoint(startPoint.x(), startPoint.y()) point2 = QgsPoint(startPoint.x(), endPoint.y()) point3 = QgsPoint(endPoint.x(), endPoint.y()) point4 = QgsPoint(endPoint.x(), startPoint.y()) self.rubberBand.addPoint(point1, False) self.rubberBand.addPoint(point2, False) self.rubberBand.addPoint(point3, False) self.rubberBand.addPoint(point4, False) self.rubberBand.addPoint(point1, True) # true to update canvas self.rubberBand.show() def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def deactivate(self): super(RectangleMapTool, self).deactivate() # self.emit(QtCore.SIGNAL("deactivated()")) self.deactivated().emit()
class SubProfileTool(QgsMapTool): """ Tool class for making a line elevation profile """ def __init__(self, iface): """ Constructor :param iface: interface """ QgsMapTool.__init__(self, iface.mapCanvas()) self.__iface = iface self.icon_path = ':/plugins/VDLTools/icons/profile_2_icon.png' self.text = QCoreApplication.translate("VDLTools", "Line for MNT profile") self.setCursor(Qt.ArrowCursor) self.__isSelected = False self.__dockWdg = None self.__rubberLine = None self.__rubberDots = None self.ownSettings = None self.__line = None self.__startVertex = None def setTool(self): """ To set the current tool as this one """ self.canvas().setMapTool(self) def activate(self): """ When the action is selected """ QgsMapTool.activate(self) self.__dockWdg = ProfileDockWidget(self.__iface) self.__iface.addDockWidget(Qt.BottomDockWidgetArea, self.__dockWdg) self.__dockWdg.closeSignal.connect(self.__closed) self.__rubberLine = QgsRubberBand(self.canvas(), QGis.Line) color = QColor("red") color.setAlphaF(0.78) self.__rubberLine.setColor(color) self.__rubberDots = QgsRubberBand(self.canvas(), QGis.Line) color = QColor("red") color.setAlphaF(0.78) self.__rubberDots.setColor(color) self.__rubberDots.setLineStyle(Qt.DotLine) def __closed(self): """ When the dock is closed """ self.__cancel() self.__iface.actionPan().trigger() def deactivate(self): """ When the action is deselected """ self.canvas().scene().removeItem(self.__rubberLine) self.__rubberLine = None if self.__dockWdg is not None: self.__dockWdg.close() QgsMapTool.deactivate(self) def __cancel(self): """ To cancel used variables """ self.__isSelected = False self.__rubberDots = None self.__line = None self.__startVertex = None def keyReleaseEvent(self, event): """ When keyboard is pressed :param event: keyboard event """ if event.key() == Qt.Key_Escape: self.__cancel() def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if self.__isSelected: dots = QgsLineStringV2() dots.addVertex(self.__startVertex) dots.addVertex(QgsPointV2(event.mapPoint())) self.__rubberDots.reset() self.__rubberDots.setToGeometry(QgsGeometry(dots.clone()), None) def canvasReleaseEvent(self, event): """ When the mouse is clicked :param event: mouse event """ if event.button() == Qt.RightButton: self.__isSelected = False self.__rubberDots.reset() self.__calculateProfile() elif event.button() == Qt.LeftButton: if not self.__isSelected: self.__isSelected = True self.__dockWdg.clearData() self.__line = QgsLineStringV2() self.__rubberLine.reset() self.__startVertex = QgsPointV2(event.mapPoint()) self.__line.addVertex(self.__startVertex) if self.__isSelected: self.__rubberLine.reset() self.__rubberLine.setToGeometry(QgsGeometry(self.__line.clone()), None) def __calculateProfile(self): """ To calculate the profile and display it """ if self.__line is None: return self.__dockWdg.clearData() if self.__line.numPoints() == 0: return points = [] for i in range(self.__line.numPoints()): points.append({'x': self.__line.pointN(i).x(), 'y': self.__line.pointN(i).y()}) self.__dockWdg.setProfiles(points, 0) self.__dockWdg.attachCurves(None, self.ownSettings, [1, 1, 1])
class DailyImagesSearchResultsWidget(RESULTS_BASE, RESULTS_WIDGET): setAOIRequested = pyqtSignal(dict) checkedCountChanged = pyqtSignal(int) def __init__(self): super().__init__() self.setupUi(self) self._p_client = PlanetClient.getInstance() self._has_more = True self._metadata_to_show = [ PlanetNodeMetadata.CLOUD_PERCENTAGE, PlanetNodeMetadata.GROUND_SAMPLE_DISTANCE, ] self._image_count = 0 self._total_count = 0 self._request = None self._local_filters = None self._response_iterator = None self.btnSaveSearch.setIcon(SAVE_ICON) self.btnSort.setIcon(SORT_ICON) self.btnAddPreview.setIcon(ADD_PREVIEW_ICON) self.btnAddPreview.setEnabled(False) self.btnSaveSearch.clicked.connect(self._save_search) self.btnAddPreview.clicked.connect(self._add_preview_clicked) self.btnSort.clicked.connect(self._sort_order_changed) self.btnSettings.clicked.connect(self._open_settings) self.lblImageCount.setOpenExternalLinks(False) self.lblImageCount.linkActivated.connect(self.load_more_link_clicked) self._aoi_box = None self._setup_request_aoi_box() self._set_widgets_visibility(False) def _set_widgets_visibility(self, search_ok): self.tree.setVisible(search_ok) self.widgetActions.setVisible(search_ok) self.widgetNoResults.setVisible(not search_ok) def search_has_been_performed(self): return self._request is not None def _open_settings(self): dlg = ResultsConfigurationDialog(self._metadata_to_show) if dlg.exec_(): self._metadata_to_show = dlg.selection self.update_image_items() def _add_preview_clicked(self): self.add_preview() @waitcursor def add_preview(self): imgs = self.selected_images() send_analytics_for_preview(imgs) create_preview_group("Selected images", imgs) def update_image_items(self): it = QTreeWidgetItemIterator(self.tree) while it.value(): item = it.value() if isinstance(item, SceneItem): w = self.tree.itemWidget(item, 0) w.set_metadata_to_show(self._metadata_to_show) it += 1 def _save_search(self): dlg = SaveSearchDialog(self._request) if dlg.exec_(): self._p_client.create_search(dlg.request_to_save) analytics_track(SAVED_SEARCH_CREATED) def sort_order(self): order = ["acquired"] if self.btnSort.isChecked(): order.append("asc") else: order.append("desc") return order def _sort_order_changed(self): self.update_request(self._request, {}) def load_more_link_clicked(self): self.load_more() @waitcursor def update_request(self, request, local_filters): self._image_count = 0 self._request = request self._local_filters = local_filters self.tree.clear() stats_request = {"interval": "year"} stats_request.update(self._request) resp = self._p_client.stats(stats_request).get() self._total_count = sum([b["count"] for b in resp["buckets"]]) if self._total_count: response = self._p_client.quick_search( self._request, page_size=TOP_ITEMS_BATCH, sort=" ".join(self.sort_order()), ) self._response_iterator = response.iter() self.load_more() self._set_widgets_visibility(True) else: self._set_widgets_visibility(False) @waitcursor def load_more(self): page = next(self._response_iterator, None) if page is not None: for i in range(self.tree.topLevelItemCount()): date_item = self.tree.topLevelItem(i) date_widget = self.tree.itemWidget(date_item, 0) date_widget.has_new = False for j in range(date_item.childCount()): satellite_item = date_item.child(j) satellite_widget = self.tree.itemWidget(satellite_item, 0) satellite_widget.has_new = False links = page.get()[page.LINKS_KEY] next_ = links.get(page.NEXT_KEY, None) self._has_more = next_ is not None images = page.get().get(page.ITEM_KEY) for i, image in enumerate(images): if self._passes_area_coverage_filter(image): sort_criteria = "acquired" date_item, satellite_item = self._find_items_for_satellite( image) date_widget = self.tree.itemWidget(date_item, 0) satellite_widget = self.tree.itemWidget(satellite_item, 0) item = SceneItem(image, sort_criteria) widget = SceneItemWidget( image, sort_criteria, self._metadata_to_show, item, self._request, ) widget.checkedStateChanged.connect( self.checked_count_changed) widget.thumbnailChanged.connect( satellite_widget.update_thumbnail) item.setSizeHint(0, widget.sizeHint()) satellite_item.addChild(item) self.tree.setItemWidget(item, 0, widget) date_widget.update_for_children() self._image_count += 1 for i in range(self.tree.topLevelItemCount()): date_item = self.tree.topLevelItem(i) date_widget = self.tree.itemWidget(date_item, 0) for j in range(date_item.childCount()): satellite_item = date_item.child(j) satellite_widget = self.tree.itemWidget(satellite_item, 0) satellite_widget.update_for_children() satellite_widget.update_thumbnail() satellite_item.sortChildren(0, Qt.AscendingOrder) date_widget.update_for_children() date_widget.update_thumbnail() self.item_count_changed() else: self._has_more = False self.item_count_changed() def _local_filter(self, name): for f in self._local_filters: if f.get("field_name") == name: return f def _passes_area_coverage_filter(self, image): area_coverage = area_coverage_for_image(image, self._request) if area_coverage is None: return True # an ID filter is begin used, so it makes no sense to # check for are acoverage filt = self._local_filter("area_coverage") if filt: minvalue = filt["config"].get("gte", 0) maxvalue = filt["config"].get("lte", 100) return area_coverage > minvalue and area_coverage < maxvalue return True def _find_item_for_date(self, image): sort_criteria = "acquired" date = iso8601.parse_date(image[PROPERTIES][sort_criteria]).date() itemtype = image[PROPERTIES][ITEM_TYPE] count = self.tree.topLevelItemCount() for i in range(count): child = self.tree.topLevelItem(i) if child.date == date and child.itemtype == itemtype: return child date_item = DateItem(image, sort_criteria) widget = DateItemWidget(image, sort_criteria, date_item) widget.checkedStateChanged.connect(self.checked_count_changed) date_item.setSizeHint(0, widget.sizeHint()) self.tree.addTopLevelItem(date_item) self.tree.setItemWidget(date_item, 0, widget) return date_item def _find_items_for_satellite(self, image): date_item = self._find_item_for_date(image) date_widget = self.tree.itemWidget(date_item, 0) satellite = image[PROPERTIES][SATELLITE_ID] instrument = image[PROPERTIES].get(INSTRUMENT, "") count = date_item.childCount() for i in range(count): child = date_item.child(i) if child.satellite == satellite: return date_item, child satellite_item = SatelliteItem(satellite) widget = SatelliteItemWidget(satellite, instrument, satellite_item) widget.thumbnailChanged.connect(date_widget.update_thumbnail) widget.checkedStateChanged.connect(self.checked_count_changed) satellite_item.setSizeHint(0, widget.sizeHint()) date_item.addChild(satellite_item) self.tree.setItemWidget(satellite_item, 0, widget) return date_item, satellite_item def selected_images(self): selected = [] it = QTreeWidgetItemIterator(self.tree) while it.value(): item = it.value() if isinstance(item, SceneItem): w = self.tree.itemWidget(item, 0) w.set_metadata_to_show(self._metadata_to_show) if w.is_selected(): selected.append(w.image) it += 1 return selected def checked_count_changed(self): numimages = len(self.selected_images()) self.btnAddPreview.setEnabled(numimages) self.checkedCountChanged.emit(numimages) def item_count_changed(self): if self._image_count < self._total_count: self.lblImageCount.setText( f"{self._image_count} images. <a href='#'>Load more</a>") else: self.lblImageCount.setText(f"{self._image_count} images") def _setup_request_aoi_box(self): self._aoi_box = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.PolygonGeometry) self._aoi_box.setFillColor(QColor(0, 0, 0, 0)) self._aoi_box.setStrokeColor(SEARCH_AOI_COLOR) self._aoi_box.setWidth(2) self._aoi_box.setLineStyle(Qt.DashLine) @pyqtSlot() def clear_aoi_box(self): if self._aoi_box: self._aoi_box.reset(QgsWkbTypes.PolygonGeometry) def clean_up(self): self.clear_aoi_box() self.tree.clear() self.lblImageCount.setText("") self._set_widgets_visibility(False) def closeEvent(self, event): self.clean_up() super().closeEvent(self, event) def request_query(self): return self._request
class SwissLocatorFilter(QgsLocatorFilter): HEADERS = { b'User-Agent': b'Mozilla/5.0 QGIS Swiss Geoportal Locator Filter' } message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget) def __init__(self, filter_type: FilterType, iface: QgisInterface = None, crs: str = None): """" :param filter_type: the type of filter :param locale_lang: the language of the locale. :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise :param crs: if iface is not given, it shall be provided, see clone() """ super().__init__() self.type = filter_type self.rubber_band = None self.feature_rubber_band = None self.iface = iface self.map_canvas = None self.settings = Settings() self.transform_ch = None self.transform_4326 = None self.map_tip = None self.current_timer = None self.crs = None self.event_loop = None self.result_found = False self.access_managers = {} self.nam_map_tip = None self.nam_fetch_feature = None if crs: self.crs = crs self.lang = get_language() self.searchable_layers = searchable_layers(self.lang, restrict=True) if iface is not None: # happens only in main thread self.map_canvas = iface.mapCanvas() self.map_canvas.destinationCrsChanged.connect( self.create_transforms) self.rubber_band = QgsRubberBand(self.map_canvas, QgsWkbTypes.PointGeometry) self.rubber_band.setColor(QColor(255, 255, 50, 200)) self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE) self.rubber_band.setIconSize(15) self.rubber_band.setWidth(4) self.rubber_band.setBrushStyle(Qt.NoBrush) self.feature_rubber_band = QgsRubberBand( self.map_canvas, QgsWkbTypes.PolygonGeometry) self.feature_rubber_band.setColor(QColor(255, 50, 50, 200)) self.feature_rubber_band.setFillColor(QColor(255, 255, 50, 160)) self.feature_rubber_band.setBrushStyle(Qt.SolidPattern) self.feature_rubber_band.setLineStyle(Qt.SolidLine) self.feature_rubber_band.setWidth(4) self.create_transforms() def name(self): return '{}_{}'.format(self.__class__.__name__, FilterType(self.type).name) def clone(self): return SwissLocatorFilter(self.type, crs=self.crs) def priority(self): return self.settings.value( '{type}_priority'.format(type=self.type.value)) def displayName(self): if self.type is FilterType.Location: return self.tr('Swiss Geoportal locations') elif self.type is FilterType.WMS: return self.tr('Swiss Geoportal / opendata.swiss WMS layers') elif self.type is FilterType.Feature: return self.tr('Swiss Geoportal features') else: raise NameError('Filter type is not valid.') def prefix(self): if self.type is FilterType.Location: return 'chl' elif self.type is FilterType.WMS: return 'chw' elif self.type is FilterType.Feature: return 'chf' else: raise NameError('Filter type is not valid.') def clearPreviousResults(self): self.rubber_band.reset(QgsWkbTypes.PointGeometry) self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry) if self.map_tip is not None: del self.map_tip self.map_tip = None if self.current_timer is not None: self.current_timer.stop() self.current_timer.deleteLater() self.current_timer = None def hasConfigWidget(self): return True def openConfigWidget(self, parent=None): dlg = ConfigDialog(parent) wid = dlg.findChild(QTabWidget, "tabWidget", Qt.FindDirectChildrenOnly) tab = wid.findChild(QWidget, self.type.value) wid.setCurrentWidget(tab) dlg.exec_() def create_transforms(self): # this should happen in the main thread self.crs = self.settings.value('crs') if self.crs == 'project': map_crs = self.map_canvas.mapSettings().destinationCrs() if map_crs.isValid(): self.crs = map_crs.authid().split(':')[1] if self.crs not in AVAILABLE_CRS: self.crs = '2056' assert self.crs in AVAILABLE_CRS src_crs_ch = QgsCoordinateReferenceSystem('EPSG:{}'.format(self.crs)) assert src_crs_ch.isValid() dst_crs = self.map_canvas.mapSettings().destinationCrs() self.transform_ch = QgsCoordinateTransform(src_crs_ch, dst_crs, QgsProject.instance()) src_crs_4326 = QgsCoordinateReferenceSystem('EPSG:4326') self.transform_4326 = QgsCoordinateTransform(src_crs_4326, dst_crs, QgsProject.instance()) def group_info(self, group: str) -> (str, str): groups = { 'zipcode': { 'name': self.tr('ZIP code'), 'layer': 'ch.swisstopo-vd.ortschaftenverzeichnis_plz' }, 'gg25': { 'name': self.tr('Municipal boundaries'), 'layer': 'ch.swisstopo.swissboundaries3d-gemeinde-flaeche.fill' }, 'district': { 'name': self.tr('District'), 'layer': 'ch.swisstopo.swissboundaries3d-bezirk-flaeche.fill' }, 'kantone': { 'name': self.tr('Cantons'), 'layer': 'ch.swisstopo.swissboundaries3d-kanton-flaeche.fill' }, 'gazetteer': { 'name': self.tr('Index'), 'layer': 'ch.swisstopo.swissnames3d' }, # there is also: ch.bav.haltestellen-oev ? 'address': { 'name': self.tr('Address'), 'layer': 'ch.bfs.gebaeude_wohnungs_register' }, 'parcel': { 'name': self.tr('Parcel'), 'layer': None } } if group not in groups: self.info('Could not find group {} in dictionary'.format(group)) return None, None return groups[group]['name'], groups[group]['layer'] @staticmethod def rank2priority(rank) -> float: """ Translate the rank from geoportal to the priority of the result see https://api3.geo.admin.ch/services/sdiservices.html#search :param rank: an integer from 1 to 7 :return: the priority as a float from 0 to 1, 1 being a perfect match """ return float(-rank / 7 + 1) @staticmethod def box2geometry(box: str) -> QgsRectangle: """ Creates a rectangle from a Box definition as string :param box: the box as a string :return: the rectangle """ coords = re.findall(r'\b(\d+(?:\.\d+)?)\b', box) if len(coords) != 4: raise InvalidBox('Could not parse: {}'.format(box)) return QgsRectangle(float(coords[0]), float(coords[1]), float(coords[2]), float(coords[3])) @staticmethod def url_with_param(url, params) -> str: url = QUrl(url) q = QUrlQuery(url) for key, value in params.items(): q.addQueryItem(key, value) url.setQuery(q) return url.url() def fetchResults(self, search: str, context: QgsLocatorContext, feedback: QgsFeedback): try: self.dbg_info("start Swiss locator search...") if len(search) < 2: return if len(search) < 4 and self.type is FilterType.Feature: return self.result_found = False swisstopo_base_url = 'https://api3.geo.admin.ch/rest/services/api/SearchServer' swisstopo_base_params = { 'type': self.type.value, 'searchText': str(search), 'returnGeometry': 'true', 'lang': self.lang, 'sr': self.crs, 'limit': str( self.settings.value( '{type}_limit'.format(type=self.type.value))) # bbox Must be provided if the searchText is not. # A comma separated list of 4 coordinates representing # the bounding box on which features should be filtered (SRID: 21781). } # Locations, WMS layers if self.type is not FilterType.Feature: nam = NetworkAccessManager() feedback.canceled.connect(nam.abort) search_urls = [(swisstopo_base_url, swisstopo_base_params)] if self.settings.value('layers_include_opendataswiss' ) and self.type is FilterType.WMS: search_urls.append( ('https://opendata.swiss/api/3/action/package_search?', { 'q': 'q=WMS+%C3' + str(search) })) for (swisstopo_base_url, swisstopo_base_params) in search_urls: swisstopo_base_url = self.url_with_param( swisstopo_base_url, swisstopo_base_params) self.dbg_info(swisstopo_base_url) try: (response, content) = nam.request(swisstopo_base_url, headers=self.HEADERS, blocking=True) self.handle_response(response, search, feedback) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(err) # Feature search else: # Feature search is split in several requests # otherwise URL is too long self.access_managers = {} try: layers = list(self.searchable_layers.keys()) assert len(layers) > 0 step = 30 for l in range(0, len(layers), step): last = min(l + step - 1, len(layers) - 1) swisstopo_base_params['features'] = ','.join( layers[l:last]) self.access_managers[self.url_with_param( swisstopo_base_url, swisstopo_base_params)] = None except IOError: self.info( 'Layers data file not found. Please report an issue.', Qgis.Critical) # init event loop # wait for all requests to end self.event_loop = QEventLoop() def reply_finished(response): self.handle_response(response, search, feedback) if response.url in self.access_managers: self.access_managers[response.url] = None for nam in self.access_managers.values(): if nam is not None: return self.event_loop.quit() feedback.canceled.connect(self.event_loop.quit) # init the network access managers, create the URL for swisstopo_base_url in self.access_managers: self.dbg_info(swisstopo_base_url) nam = NetworkAccessManager() self.access_managers[swisstopo_base_url] = nam nam.finished.connect(reply_finished) nam.request(swisstopo_base_url, headers=self.HEADERS, blocking=False) feedback.canceled.connect(nam.abort) # Let the requests end and catch all exceptions (and clean up requests) if len(self.access_managers) > 0: try: self.event_loop.exec_( QEventLoop.ExcludeUserInputEvents) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(str(err)) if not self.result_found: result = QgsLocatorResult() result.filter = self result.displayString = self.tr('No result found.') result.userData = NoResult().as_definition() self.resultFetched.emit(result) except Exception as e: self.info(e, Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] self.info( '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) self.info( traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def handle_response(self, response, search: str, feedback: QgsFeedback): try: if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in main response with status code: {} from {}". format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) # self.dbg_info(data) if self.is_opendata_swiss_response(data): visited_capabilities = [] for loc in data['result']['results']: display_name = loc['title'].get(self.lang, "") if not display_name: # Fallback to german display_name = loc['title']['de'] for res in loc['resources']: url = res['url'] url_components = urlparse(url) wms_url = url_components.scheme + '://' + url_components.netloc + '/' + url_components.path + '?' result = QgsLocatorResult() result.filter = self result.group = 'opendata.swiss' result.icon = QgsApplication.getThemeIcon( "/mActionAddWmsLayer.svg") if 'wms' in url.lower(): if res['media_type'] == 'WMS': result.displayString = display_name result.description = url if res['title']['de'] == 'GetMap': layers = parse_qs( url_components.query)['LAYERS'] result.userData = WMSLayerResult( layer=layers[0], title=display_name, url=wms_url).as_definition() self.result_found = True self.resultFetched.emit(result) elif 'request=getcapabilities' in url.lower( ) and url_components.netloc not in visited_capabilities: visited_capabilities.append( url_components.netloc) def parse_capabilities_result(response): capabilities = ET.fromstring( response.content) # Get xml namespace match = re.match(r'\{.*\}', capabilities.tag) namespace = match.group(0) if match else '' # Search for layers containing the search term in the name or title for layer in capabilities.findall( './/{}Layer'.format(namespace)): layername = self.find_text( layer, '{}Name'.format(namespace)) layertitle = self.find_text( layer, '{}Title'.format(namespace)) if layername and ( search in layername.lower() or search in layertitle.lower()): if not layertitle: layertitle = layername result.displayString = layertitle result.description = '{}?LAYERS={}'.format( url.replace( 'GetCapabilities', 'GetMap'), layername) result.userData = WMSLayerResult( layer=layername, title=layertitle, url=wms_url).as_definition() self.result_found = True self.resultFetched.emit(result) self.event_loop.quit() # Retrieve Capabilities xml self.event_loop = QEventLoop() nam = NetworkAccessManager() nam.finished.connect(parse_capabilities_result) nam.request(url, headers=self.HEADERS, blocking=False) feedback.canceled.connect(self.event_loop.quit) try: self.event_loop.exec_( QEventLoop.ExcludeUserInputEvents) except RequestsExceptionUserAbort: pass except RequestsException as err: self.info(err) else: for loc in data['results']: self.dbg_info("keys: {}".format(loc['attrs'].keys())) result = QgsLocatorResult() result.filter = self result.group = 'Swiss Geoportal' if loc['attrs']['origin'] == 'layer': # available keys: ['origin', 'lang', 'layer', 'staging', 'title', 'topics', 'detail', 'label', 'id'] for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) result.displayString = loc['attrs']['title'] result.description = loc['attrs']['layer'] result.userData = WMSLayerResult( layer=loc['attrs']['layer'], title=loc['attrs']['title'], url='http://wms.geo.admin.ch/?VERSION%3D2.0.0' ).as_definition() result.icon = QgsApplication.getThemeIcon( "/mActionAddWmsLayer.svg") self.result_found = True self.resultFetched.emit(result) elif loc['attrs']['origin'] == 'feature': for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) layer = loc['attrs']['layer'] point = QgsPointXY(loc['attrs']['lon'], loc['attrs']['lat']) if layer in self.searchable_layers: layer_display = self.searchable_layers[layer] else: self.info( self. tr('Layer {} is not in the list of searchable layers.' ' Please report issue.'.format(layer)), Qgis.Warning) layer_display = layer result.group = layer_display result.displayString = loc['attrs']['detail'] result.userData = FeatureResult( point=point, layer=layer, feature_id=loc['attrs'] ['feature_id']).as_definition() result.icon = QIcon( ":/plugins/swiss_locator/icons/swiss_locator.png") self.result_found = True self.resultFetched.emit(result) else: # locations for key, val in loc['attrs'].items(): self.dbg_info('{}: {}'.format(key, val)) group_name, group_layer = self.group_info( loc['attrs']['origin']) if 'layerBodId' in loc['attrs']: self.dbg_info("layer: {}".format( loc['attrs']['layerBodId'])) if 'featureId' in loc['attrs']: self.dbg_info("feature: {}".format( loc['attrs']['featureId'])) result.displayString = strip_tags( loc['attrs']['label']) # result.description = loc['attrs']['detail'] # if 'featureId' in loc['attrs']: # result.description = loc['attrs']['featureId'] result.group = group_name result.userData = LocationResult( point=QgsPointXY(loc['attrs']['y'], loc['attrs']['x']), bbox=self.box2geometry( loc['attrs']['geom_st_box2d']), layer=group_layer, feature_id=loc['attrs']['featureId'] if 'featureId' in loc['attrs'] else None, html_label=loc['attrs']['label']).as_definition() result.icon = QIcon( ":/plugins/swiss_locator/icons/swiss_locator.png") self.result_found = True self.resultFetched.emit(result) except Exception as e: self.info(str(e), Qgis.Critical) exc_type, exc_obj, exc_traceback = sys.exc_info() filename = os.path.split( exc_traceback.tb_frame.f_code.co_filename)[1] self.info( '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical) self.info( traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical) def triggerResult(self, result: QgsLocatorResult): # this should be run in the main thread, i.e. mapCanvas should not be None # remove any map tip self.clearPreviousResults() user_data = NoResult try: swiss_result = result_from_data(result) except SystemError: self.message_emitted.emit( self.displayName(), self. tr('QGIS Swiss Locator encountered an error. Please <b>update to QGIS 3.16.2</b> or newer.' ), Qgis.Warning, None) if type(swiss_result) == NoResult: return # WMS if type(swiss_result) == WMSLayerResult: url_with_params = 'contextualWMSLegend=0' \ '&crs=EPSG:{crs}' \ '&dpiMode=7' \ '&featureCount=10' \ '&format=image/png' \ '&layers={layer}' \ '&styles=' \ '&url={url}' \ .format(crs=self.crs, layer=swiss_result.layer, url=swiss_result.url) wms_layer = QgsRasterLayer(url_with_params, result.displayString, 'wms') label = QLabel() label.setTextFormat(Qt.RichText) label.setTextInteractionFlags(Qt.TextBrowserInteraction) label.setOpenExternalLinks(True) if 'geo.admin.ch' in swiss_result.url.lower(): label.setText( '<a href="https://map.geo.admin.ch/' '?lang={}&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">' 'Open layer in map.geo.admin.ch</a>'.format( self.lang, swiss_result.layer)) if not wms_layer.isValid(): msg = self.tr('Cannot load WMS layer: {} ({})'.format( swiss_result.title, swiss_result.layer)) level = Qgis.Warning self.info(msg, level) else: msg = self.tr('WMS layer added to the map: {} ({})'.format( swiss_result.title, swiss_result.layer)) level = Qgis.Info QgsProject.instance().addMapLayer(wms_layer) self.message_emitted.emit(self.displayName(), msg, level, label) # Feature elif type(swiss_result) == FeatureResult: point = QgsGeometry.fromPointXY(swiss_result.point) point.transform(self.transform_4326) self.highlight(point) if self.settings.value('show_map_tip'): self.show_map_tip(swiss_result.layer, swiss_result.feature_id, point) # Location else: point = QgsGeometry.fromPointXY(swiss_result.point) if swiss_result.bbox.isNull(): bbox = None else: bbox = QgsGeometry.fromRect(swiss_result.bbox) bbox.transform(self.transform_ch) layer = swiss_result.layer feature_id = swiss_result.feature_id if not point: return point.transform(self.transform_ch) self.highlight(point, bbox) if layer and feature_id: self.fetch_feature(layer, feature_id) if self.settings.value('show_map_tip'): self.show_map_tip(layer, feature_id, point) else: self.current_timer = QTimer() self.current_timer.timeout.connect(self.clearPreviousResults) self.current_timer.setSingleShot(True) self.current_timer.start(5000) def highlight(self, point, bbox=None): if bbox is None: bbox = point self.rubber_band.reset(QgsWkbTypes.PointGeometry) self.rubber_band.addGeometry(point, None) rect = bbox.boundingBox() rect.scale(1.1) self.map_canvas.setExtent(rect) self.map_canvas.refresh() def fetch_feature(self, layer, feature_id): # Try to get more info self.nam_fetch_feature = NetworkAccessManager() url_detail = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}' \ .format(layer=layer, feature_id=feature_id) params = {'lang': self.lang, 'sr': self.crs} url_detail = self.url_with_param(url_detail, params) self.dbg_info(url_detail) self.nam_fetch_feature.finished.connect(self.parse_feature_response) self.nam_fetch_feature.request(url_detail, headers=self.HEADERS, blocking=False) def parse_feature_response(self, response): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in feature response with status code: {} from {}". format(response.status_code, response.url)) return data = json.loads(response.content.decode('utf-8')) self.dbg_info(data) if 'feature' not in data or 'geometry' not in data['feature']: return if 'rings' in data['feature']['geometry']: rings = data['feature']['geometry']['rings'] self.dbg_info(rings) for r in range(0, len(rings)): for p in range(0, len(rings[r])): rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1]) geometry = QgsGeometry.fromPolygonXY(rings) geometry.transform(self.transform_ch) self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry) self.feature_rubber_band.addGeometry(geometry, None) def show_map_tip(self, layer, feature_id, point): if layer and feature_id: url_html = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}/htmlPopup' \ .format(layer=layer, feature_id=feature_id) params = {'lang': self.lang, 'sr': self.crs} url_html = self.url_with_param(url_html, params) self.dbg_info(url_html) self.nam_map_tip = NetworkAccessManager() self.nam_map_tip.finished.connect( lambda response: self.parse_map_tip_response(response, point)) self.nam_map_tip.request(url_html, headers=self.HEADERS, blocking=False) def parse_map_tip_response(self, response, point): if response.status_code != 200: if not isinstance(response.exception, RequestsExceptionUserAbort): self.info( "Error in map tip response with status code: {} from {}". format(response.status_code, response.url)) return self.dbg_info(response.content.decode('utf-8')) self.map_tip = MapTip(self.iface, response.content.decode('utf-8'), point.asPoint()) self.map_tip.closed.connect(self.clearPreviousResults) def info(self, msg="", level=Qgis.Info): self.logMessage(str(msg), level) def dbg_info(self, msg=""): if DEBUG: self.info(msg) @staticmethod def break_camelcase(identifier): matches = re.finditer( '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', identifier) return ' '.join([m.group(0) for m in matches]) def is_opendata_swiss_response(self, json): return 'opendata.swiss' in json.get("help", []) def find_text(self, xmlElement, match): node = xmlElement.find(match) return node.text if node is not None else ''