Example #1
0
    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)
Example #3
0
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
Example #5
0
 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
Example #7
0
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 )
Example #8
0
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
Example #10
0
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
Example #11
0
 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
Example #12
0
 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
Example #13
0
    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)
Example #14
0
    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)
Example #15
0
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()
Example #16
0
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)
Example #17
0
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
Example #18
0
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])
Example #19
0
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
Example #21
0
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
Example #23
0
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)
Example #26
0
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)
Example #27
0
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
Example #28
0
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
Example #29
0
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()"))
Example #30
0
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
Example #32
0
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()
Example #33
0
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
Example #34
0
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()
Example #35
0
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()
Example #36
0
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()
Example #39
0
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 ''