class PlaceMarkerMapTool(QgsMapToolEmitPoint):
    '''
    classdocs
    '''

    def __init__(self, canvas):
        '''
        Constructor
        '''
        self.canvas = canvas
        super(PlaceMarkerMapTool, self).__init__(self.canvas)
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.rubberBand.setColor(Qt.red)
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.rubberBand.reset(QGis.Polygon)

    def canvasReleaseEvent(self, e):
        p1 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() - 2)
        p2 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() - 2)
        p3 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() + 2, e.y() + 2)
        p4 = self.canvas.getCoordinateTransform().toMapCoordinates(e.x() - 2, e.y() + 2)
        self.reset()

        self.rubberBand.addPoint(p1, False)
        self.rubberBand.addPoint(p2, False)
        self.rubberBand.addPoint(p3, False)
        self.rubberBand.addPoint(p4, True)
        self.rubberBand.show()

    def deactivate(self):
        self.reset()
        super(PlaceMarkerMapTool, self).deactivate()
class LineDrawer(QgsMapToolEmitPoint):

    def __init__(self, canvas):
        # call the parent constructor
        QgsMapToolEmitPoint.__init__(self, canvas)
        # store the passed canvas
        self.canvas = canvas

        # flag to know whether the tool is performing a drawing operation 
        self.isDrawing = False

        # create and setup the rubber band to display the line
        self.rubberBand = QgsRubberBand( self.canvas, False )    # False = not a polygon = a line
        self.rubberBand.setColor( Qt.red )
        self.rubberBand.setWidth( 1 )


    def clear(self):
        self.rubberBand.reset( False )    # False = not a polygon = a line

    def delete(self):
        self.canvas.scene().removeItem( self.rubberBand )

    def canvasPressEvent(self, e):
        # which the mouse button?
        if e.button() == Qt.LeftButton:
            # left click

            # if it's the first left click, clear the rubberband 
            if not self.isDrawing:
                self.clear()

            # we are drawing now
            self.isDrawing = True

            # convert the clicked position to map coordinates
            point = self.toMapCoordinates( e.pos() )
            # add a new point to the rubber band
            self.rubberBand.addPoint( point, True )    # True = display updates on the canvas
            # and finally show the rubber band
            self.rubberBand.show()

        elif e.button() == Qt.RightButton:
            # right click, stop drawing
            self.isDrawing = False
            # emit a signal
            self.emit( SIGNAL("editingFinished()") )

    def canvasMoveEvent(self, e):
        # check if it's already drawing
        if not self.isDrawing:
            return

        # convert the mouse position to map coordinates
        point = self.toMapCoordinates( e.pos() )
        # move the last point to the new coordinates
        self.rubberBand.movePoint( point )

    def geometry(self):
        return self.rubberBand.asGeometry()
class GetPointMapTool(QgsMapToolEmitPoint):

    coordCaptured = ""

    def __init__(self, canvas, iface, dockwidget, currentMapTool):
        self.canvas = canvas
        self.iface = iface
        self.currentMapTool = currentMapTool
        self.dockwidget = dockwidget
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Point)
        self.rubberBand.setColor(QColor(255,5,5))
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        self.point = self.toMapCoordinates(e.pos())
        self.isEmittingPoint = True
        self.showPoint(self.point)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        self.coordCaptured = self.pointdef()
        if self.coordCaptured is not None:
            print "Point:", self.coordCaptured
            self.coordCaptured = str(self.coordCaptured).strip("(")
            self.coordCaptured = str(self.coordCaptured).strip(")")
            self.dockwidget.munLineEdit.setText(self.coordCaptured)


        self.iface.mapCanvas().setMapTool(self.currentMapTool)

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
       # self.showRect(self.startPoint, self.endPoint)

    def showPoint(self, point):
        self.rubberBand.reset(QGis.Polygon)


        point1 = QgsPoint(point.x(), point.y())


        self.rubberBand.addPoint(point1, False)

        self.rubberBand.show()

    def pointdef(self):

        return QgsPoint(self.point)
Example #4
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
 def highlight(self,geometry):
     def processEvents():
         try:
             qApp.processEvents()
         except:
             QApplication.processEvents()
             
     highlight = QgsRubberBand(self.canvas, geometry.type())
     highlight.setColor(QColor("#36AF6C"))
     highlight.setFillColor(QColor("#36AF6C"))
     highlight.setWidth(2)
     highlight.setToGeometry(geometry,self.canvas.currentLayer())
     processEvents()
     sleep(.1)
     highlight.hide()
     processEvents()
     sleep(.1)
     highlight.show()
     processEvents()
     sleep(.1)
     highlight.reset()
     processEvents()
Example #6
0
class MapWidget(Ui_CanvasWidget, QMainWindow):
    def __init__(self, parent=None):
        super(MapWidget, self).__init__(parent)
        self.setupUi(self)

        icon = roam_style.iconsize()
        self.projecttoolbar.setIconSize(QSize(icon, icon))

        self.firstshow = True
        self.layerbuttons = []
        self.editfeaturestack = []
        self.lastgpsposition = None
        self.project = None
        self.gps = None
        self.gpslogging = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))

        self.bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), self.canvas)
        self.bridge.setAutoSetupOnFirstLayer(False)
        QgsProject.instance().writeProject.connect(self.bridge.writeProject)
        QgsProject.instance().readProject.connect(self.bridge.readProject)

        # self.canvas.setInteractive(False)
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)

        self.snapping = SnappingUtils(self.canvas, self)
        self.canvas.setSnappingUtils(self.snapping)
        QgsProject.instance().readProject.connect(self.snapping.readConfigFromProject)

        if hasattr(self.canvas, 'setParallelRenderingEnabled'):
            threadcount = QThread.idealThreadCount()
            threadcount = 2 if threadcount > 2 else 1
            QgsApplication.setMaxThreads(threadcount)
            self.canvas.setParallelRenderingEnabled(True)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)

        self.editgroup = QActionGroup(self)
        self.editgroup.setExclusive(True)
        self.editgroup.addAction(self.actionPan)
        self.editgroup.addAction(self.actionZoom_In)
        self.editgroup.addAction(self.actionZoom_Out)
        self.editgroup.addAction(self.actionInfo)

        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self)
        self.projecttoolbar.addAction(self.actionGPS)

        if roam.config.settings.get('north_arrow', False):
            self.northarrow = NorthArrow(":/icons/north", self.canvas)
            self.northarrow.setPos(10, 10)
            self.canvas.scene().addItem(self.northarrow)

        self.scalebar_enabled = roam.config.settings.get('scale_bar', False)
        if self.scalebar_enabled:
            self.scalebar = ScaleBarItem(self.canvas)
            self.canvas.scene().addItem(self.scalebar)

        self.projecttoolbar.setContextMenuPolicy(Qt.CustomContextMenu)

        gpsspacewidget = QWidget()
        gpsspacewidget.setMinimumWidth(30)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        self.dataentryselection = QAction(self.projecttoolbar)
        self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection)
        self.dataentryselection.triggered.connect(self.select_data_entry)

        self.marker = GPSMarker(self.canvas)
        self.marker.hide()

        self.currentfeatureband = CurrentSelection(self.canvas)
        self.currentfeatureband.setIconSize(30)
        self.currentfeatureband.setWidth(10)
        self.currentfeatureband.setColor(QColor(186, 93, 212, 50))
        self.currentfeatureband.setOutlineColour(QColor(186, 93, 212))

        self.gpsband = QgsRubberBand(self.canvas)
        self.gpsband.setColor(QColor(165, 111, 212, 75))
        self.gpsband.setWidth(5)

        RoamEvents.editgeometry.connect(self.queue_feature_for_edit)
        RoamEvents.selectioncleared.connect(self.clear_selection)
        RoamEvents.selectionchanged.connect(self.highlight_selection)
        RoamEvents.openfeatureform.connect(self.feature_form_loaded)
        RoamEvents.sync_complete.connect(self.refresh_map)
        RoamEvents.snappingChanged.connect(self.snapping_changed)

        self.snappingbutton = QToolButton()
        self.snappingbutton.setText("Snapping: On")
        self.snappingbutton.setAutoRaise(True)
        self.snappingbutton.pressed.connect(self.toggle_snapping)

        spacer = QWidget()
        spacer2 = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        spacer2.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.scalewidget = QgsScaleComboBox()

        self.scalebutton = QToolButton()
        self.scalebutton.setAutoRaise(True)
        self.scalebutton.setMaximumHeight(self.statusbar.height())
        self.scalebutton.pressed.connect(self.selectscale)
        self.scalebutton.setText("Scale")

        self.scalelist = BigList(parent=self.canvas, centeronparent=True, showsave=False)
        self.scalelist.hide()
        self.scalelist.setlabel("Map Scale")
        self.scalelist.setmodel(self.scalewidget.model())
        self.scalelist.closewidget.connect(self.scalelist.close)
        self.scalelist.itemselected.connect(self.update_scale_from_item)
        self.scalelist.itemselected.connect(self.scalelist.close)

        self.positionlabel = QLabel('')
        self.gpslabel = QLabel("GPS: Not active")

        self.statusbar.addWidget(self.snappingbutton)
        self.statusbar.addWidget(spacer2)
        self.statusbar.addWidget(self.gpslabel)
        self.statusbar.addPermanentWidget(self.scalebutton)

        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.canvas.scaleChanged.connect(self.updatestatuslabel)

        GPS.gpsposition.connect(self.update_gps_label)
        GPS.gpsdisconnected.connect(self.gps_disconnected)

        self.connectButtons()

    def clear_plugins(self):
        toolbars = self.findChildren(QToolBar)
        for toolbar in toolbars:
            if toolbar.property("plugin_toolbar"):
                self.removeToolBar(toolbar)
                toolbar.deleteLater()

    def add_plugins(self, pluginnames):
        for name in pluginnames:
            # Get the plugin
            try:
                plugin_mod = plugins.loaded_plugins[name]
            except KeyError:
                continue

            if not hasattr(plugin_mod, 'toolbars'):
                roam.utils.warning("No toolbars() function found in {}".format(name))
                continue

            toolbars = plugin_mod.toolbars()
            self.load_plugin_toolbars(toolbars)

    def load_plugin_toolbars(self, toolbars):
        for ToolBarClass in toolbars:
            toolbar = ToolBarClass(plugins.api, self)
            self.addToolBar(Qt.BottomToolBarArea, toolbar)
            toolbar.setProperty("plugin_toolbar", True)

    def snapping_changed(self, snapping):
        """
        Called when the snapping settings have changed. Updates the label in the status bar.
        :param snapping:
        """
        if snapping:
            self.snappingbutton.setText("Snapping: On")
        else:
            self.snappingbutton.setText("Snapping: Off")

    def toggle_snapping(self):
        """
        Toggle snapping on or off.
        """
        snap = not snapping
        global snapping
        snapping = snap
        RoamEvents.snappingChanged.emit(snapping)

    def selectscale(self):
        """
        Show the select scale widget.
        :return:
        """
        self.scalelist.show()

    def update_scale_from_item(self, index):
        """
        Update the canvas scale from the selected scale item.
        :param index: The index of the selected item.
        """
        scale, _ = self.scalewidget.toDouble(index.data(Qt.DisplayRole))
        self.canvas.zoomScale(1.0 / scale)

    def update_gps_label(self, position, gpsinfo):
        """
        Update the GPS label in the status bar with the GPS status.
        :param position: The current GPS position.
        :param gpsinfo: The current extra GPS information.
        """
        self.gpslabel.setText("GPS: PDOP {0:.2f}   HDOP {1:.2f}    VDOP {2:.2f}".format(gpsinfo.pdop,
                                                                                        gpsinfo.hdop,
                                                                                        gpsinfo.vdop))

    def gps_disconnected(self):
        """
        Called when the GPS is disconnected. Updates the label in the status bar with the message.
        :return:
        """
        self.gpslabel.setText("GPS Not Active")

    def updatestatuslabel(self, *args):
        """
        Update the status bar labels when the information has changed.
        """
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))
        scale = 1.0 / self.canvas.scale()
        scale = self.scalewidget.toString(scale)
        self.scalebutton.setText(scale)

    def refresh_map(self):
        """
        Refresh the map
        """
        self.canvas.refresh()

    def updatescale(self):
        """
        Update the scale of the map with the current scale from the scale widget
        :return:
        """
        self.canvas.zoomScale(1.0 / self.scalewidget.scale())

    def init_qgisproject(self, doc):
        """
        Called when the project file is read for the firs time.
        :param doc: The XML doc.
        :return: The current canvas CRS
        :note: This method is old and needs to be refactored into something else.
        """
        return self.canvas.mapSettings().destinationCrs()

    def showEvent(self, *args, **kwargs):
        """
        Handle the show event of the of the map widget. We have to do a little hack here to make the QGIS map refresh.
        """
        if QGis.QGIS_VERSION_INT == 20200 and self.firstshow:
            self.canvas.refresh()
            self.canvas.repaint()
            self.firstshow = False

    def feature_form_loaded(self, form, feature, *args):
        """
        Called when the feature form is loaded.
        :param form: The Form object. Holds a reference to the forms layer.
        :param feature: The current capture feature
        """
        self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer)

    def highlight_selection(self, results):
        """
        Highlight the selection on the canvas.  This updates all selected objects based on the result set.
        :param results: A dict-of-list of layer-features.
        """
        self.clear_selection()
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0))
            band.setIconSize(25)
            band.setWidth(5)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            band.setZValue(self.currentfeatureband.zValue() - 1)
            for feature in features:
                band.addGeometry(feature.geometry(), layer)
        self.canvas.update()

    def highlight_active_selection(self, layer, feature, features):
        """
        Update the current active selected feature.
        :param layer: The layer of the active feature.
        :param feature: The active feature.
        :param features: The other features in the set to show as non active selection.
        :return:
        """
        self.clear_selection()
        self.highlight_selection({layer: features})
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)
        self.canvas.update()

    def clear_selection(self):
        """
        Clear the selection from the canvas.   Resets all selection rubbber bands.
        :return:
        """
        # Clear the main selection rubber band
        self.canvas.scene().update()
        self.currentfeatureband.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

        self.canvas.update()
        self.editfeaturestack = []

    def queue_feature_for_edit(self, form, feature):
        """
        Push a feature on the edit stack so the feature can have the geometry edited.
        :note: This is a big hack and I don't like it!
        :param form: The form for the current feature
        :param feature: The active feature.
        """
        def trigger_default_action():
            for action in self.projecttoolbar.actions():
                if action.property('dataentry') and action.isdefault:
                    action.trigger()
                    self.canvas.mapTool().setEditMode(True, feature.geometry())
                    break

        self.editfeaturestack.append((form, feature))
        self.load_form(form)
        trigger_default_action()

    def clear_temp_objects(self):
        """
        Clear all temp objects from the canvas.
        :return:
        """
        def clear_tool_band():
            """
            Clear the rubber band of the active tool if it has one
            """
            tool = self.canvas.mapTool()
            try:
                tool.clearBand()
            except AttributeError:
                # No clearBand method found, but that's cool.
                pass

        self.currentfeatureband.reset()
        clear_tool_band()

    def settings_updated(self, settings):
        """
        Called when the settings have been updated in the Roam config.
        :param settings: A dict of the settings.
        """
        self.actionGPS.updateGPSPort()
        gpslogging = settings.get('gpslogging', True)
        if self.gpslogging:
            self.gpslogging.logging = gpslogging

    def set_gps(self, gps, logging):
        """
        Set the GPS for the map widget.  Connects GPS signals
        """
        self.gps = gps
        self.gpslogging = logging
        self.gps.gpsposition.connect(self.gps_update_canvas)
        self.gps.firstfix.connect(self.gps_first_fix)
        self.gps.gpsdisconnected.connect(self.gps_disconnected)

    def gps_update_canvas(self, position, gpsinfo):
        """
        Updates the map canvas based on the GPS position.  By default if the GPS is outside the canvas
        extent the canvas will move to center on the GPS.  Can be turned off in settings.
        :param postion: The current GPS position.
        :param gpsinfo: The extra GPS information
        """
        # Recenter map if we go outside of the 95% of the area
        if self.gpslogging.logging:
            self.gpsband.addPoint(position)
            self.gpsband.show()

        if roam.config.settings.get('gpscenter', True):
            if not self.lastgpsposition == position:
                self.lastposition = position
                rect = QgsRectangle(position, position)
                extentlimt = QgsRectangle(self.canvas.extent())
                extentlimt.scale(0.95)

                if not extentlimt.contains(position):
                    self.zoom_to_location(position)

        self.marker.show()
        self.marker.setCenter(position, gpsinfo)

    def gps_first_fix(self, postion, gpsinfo):
        """
        Called the first time the GPS gets a fix.  If set this will zoom to the GPS after the first fix
        :param postion: The current GPS position.
        :param gpsinfo: The extra GPS information
        """
        zoomtolocation = roam.config.settings.get('gpszoomonfix', True)
        if zoomtolocation:
            self.canvas.zoomScale(1000)
            self.zoom_to_location(postion)

    def zoom_to_location(self, position):
        """
        Zoom to ta given position on the map..
        """
        rect = QgsRectangle(position, position)
        self.canvas.setExtent(rect)
        self.canvas.refresh()

    def gps_disconnected(self):
        """
        Called when the GPS is disconnected
        """
        self.marker.hide()

    def select_data_entry(self):
        """
        Open the form selection widget to allow the user to pick the active capture form.
        """
        def showformerror(form):
            pass

        def actions():
            for form in self.project.forms:
                if not self.form_valid_for_capture(form):
                    continue

                action = form.createuiaction()
                valid, failreasons = form.valid
                if not valid:
                    roam.utils.warning("Form {} failed to load".format(form.label))
                    roam.utils.warning("Reasons {}".format(failreasons))
                    action.triggered.connect(partial(showformerror, form))
                else:
                    action.triggered.connect(partial(self.load_form, form))
                yield action

        formpicker = PickActionDialog(msg="Select data entry form", wrap=5)
        formpicker.addactions(actions())
        formpicker.exec_()

    def project_loaded(self, project):
        """
        Called when the project is loaded. Main entry point for a loade project.
        :param project: The Roam project that has been loaded.
        """
        self.project = project
        self.actionPan.trigger()
        firstform = self.first_capture_form()
        if firstform:
            self.load_form(firstform)
            self.dataentryselection.setVisible(True)
        else:
            self.dataentryselection.setVisible(False)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers)
        self.actionRaster.setEnabled(hasrasters)
        self.defaultextent = self.canvas.extent()
        roam.utils.info("Extent: {}".format(self.defaultextent.toString()))

        self.infoTool.selectionlayers = project.selectlayersmapping()

        self.canvas.refresh()

        projectscales, _ = QgsProject.instance().readBoolEntry("Scales", "/useProjectScales")
        if projectscales:
            projectscales, _ = QgsProject.instance().readListEntry("Scales", "/ScalesList")

            self.scalewidget.updateScales(projectscales)
        else:
            scales = ["1:50000", "1:25000", "1:10000", "1:5000",
                      "1:2500", "1:1000", "1:500", "1:250", "1:200", "1:100"]
            scales = roam.config.settings.get('scales', scales)
            self.scalewidget.updateScales(scales)

        if self.scalebar_enabled:
            self.scalebar.update()

        self.actionPan.toggle()
        self.clear_plugins()
        self.add_plugins(project.enabled_plugins)

    def setMapTool(self, tool, *args):
        """
        Set the active map tool in the canvas.
        :param tool: The QgsMapTool to set.
        """
        if tool == self.canvas.mapTool():
            return

        if hasattr(tool, "setSnapping"):
            tool.setSnapping(snapping)
        self.canvas.setMapTool(tool)

    def connectButtons(self):
        """
        Connect the default buttons in the interface. Zoom, pan, etc
        """
        def connectAction(action, tool):
            action.toggled.connect(partial(self.setMapTool, tool))

        def cursor(name):
            pix = QPixmap(name)
            pix = pix.scaled(QSize(24, 24))
            return QCursor(pix)

        self.zoomInTool = QgsMapToolZoom(self.canvas, False)
        self.zoomOutTool = QgsMapToolZoom(self.canvas, True)
        self.panTool = PanTool(self.canvas)
        self.infoTool = InfoTool(self.canvas)

        connectAction(self.actionZoom_In, self.zoomInTool)
        connectAction(self.actionZoom_Out, self.zoomOutTool)
        connectAction(self.actionPan, self.panTool)
        connectAction(self.actionInfo, self.infoTool)

        self.zoomInTool.setCursor(cursor(':/icons/in'))
        self.zoomOutTool.setCursor(cursor(':/icons/out'))
        self.infoTool.setCursor(cursor(':/icons/select'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)
        self.actionHome.triggered.connect(self.homeview)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def form_valid_for_capture(self, form):
        """
        Check if the given form is valid for capture.
        :param form: The form to check.
        :return: True if valid form for capture
        """
        return form.has_geometry and self.project.layer_can_capture(form.QGISLayer)

    def first_capture_form(self):
        """
        Return the first valid form for capture.
        """
        for form in self.project.forms:
            if self.form_valid_for_capture(form):
                return form

    def load_form(self, form):
        """
        Load the given form so it's the active one for capture
        :param form: The form to load
        """
        self.clearCapatureTools()
        self.dataentryselection.setIcon(QIcon(form.icon))
        self.dataentryselection.setText(form.icontext)
        self.create_capture_buttons(form)

    def create_capture_buttons(self, form):
        """
        Create the capture buttons in the toolbar for the given form.
        :param form: The active form.
        """
        layer = form.QGISLayer
        tool = form.getMaptool()(self.canvas)
        for action in tool.actions:
            # Create the action here.
            if action.ismaptool:
                action.toggled.connect(partial(self.setMapTool, tool))

            # Set the action as a data entry button so we can remove it later.
            action.setProperty("dataentry", True)
            self.editgroup.addAction(action)
            self.layerbuttons.append(action)
            self.projecttoolbar.insertAction(self.topspaceraction, action)
            action.setChecked(action.isdefault)

        if hasattr(tool, 'geometryComplete'):
            add = partial(self.add_new_feature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showUIMessage, form.label))

    def add_new_feature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        # TODO Extract into function.
        # NOTE This function is doing too much, acts as add and also edit.
        layer = form.QGISLayer
        if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]:
            geometry.convertToMultiType()

        try:
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        feature = form.new_feature(geometry=geometry)
        RoamEvents.load_feature_form(form, feature, editmode=False)

    def editfeaturegeometry(self, form, feature, newgeometry):
        # TODO Extract into function.
        layer = form.QGISLayer
        layer.startEditing()
        feature.setGeometry(newgeometry)
        layer.updateFeature(feature)
        saved = layer.commitChanges()
        if not saved:
            map(roam.utils.error, layer.commitErrors())
        self.canvas.refresh()
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)
        RoamEvents.editgeometry_complete.emit(form, feature)
        self.canvas.mapTool().setEditMode(False, None)

    def clearCapatureTools(self):
        """
        Clear the capture tools from the toolbar.
        :return: True if the capture button was active at the time of clearing.
        """
        captureselected = False
        for action in self.projecttoolbar.actions():
            if action.objectName() == "capture" and action.isChecked():
                captureselected = True

            if action.property('dataentry'):
                self.projecttoolbar.removeAction(action)
        return captureselected

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        # Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        tree = QgsProject.instance().layerTreeRoot()
        for node in tree.findLayers():
            if node.layer().type() == QgsMapLayer.RasterLayer:
                if node.isVisible() == Qt.Checked:
                    state = Qt.Unchecked
                else:
                    state = Qt.Checked
                node.setVisible(state)

        self.canvas.freeze(False)
        self.canvas.refresh()

    def cleanup(self):
        """
        Clean up when the project has changed.
        :return:
        """
        self.bridge.clear()
        self.gpsband.reset()
        self.gpsband.hide()
        self.clear_selection()
        self.clear_temp_objects()
        self.clearCapatureTools()
        self.canvas.freeze()
        self.canvas.clear()
        self.canvas.freeze(False)
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)
Example #7
0
class RectangleMapTool(QgsMapToolEmitPoint):

    rectangleCreated = pyqtSignal()
    deactivated = pyqtSignal()

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(2)

        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

    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
        if self.rectangle() is not None:
            self.rectangleCreated.emit()

    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(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        # True to update canvas
        self.rubberBand.addPoint(point4, True)
        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 setRectangle(self, rect):
        if rect == self.rectangle():
            return False

        if rect is None:
            self.reset()
        else:
            self.startPoint = QgsPointXY(rect.xMaximum(), rect.yMaximum())
            self.endPoint = QgsPointXY(rect.xMinimum(), rect.yMinimum())
            self.showRect(self.startPoint, self.endPoint)
        return True

    def deactivate(self):
        QgsMapTool.deactivate(self)
        self.deactivated.emit()
class GeorefRasterBy2PointsMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rasterShadow = RasterShadowMapCanvasItem(self.canvas)

        self.firstPoint = None

        self.rubberBandOrigin = QgsRubberBand(
            self.canvas, QgsWkbTypes.PointGeometry)
        self.rubberBandOrigin.setColor(Qt.red)
        self.rubberBandOrigin.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubberBandOrigin.setIconSize(7)
        self.rubberBandOrigin.setWidth(2)

        self.rubberBandDisplacement = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandDisplacement.setColor(Qt.red)
        self.rubberBandDisplacement.setWidth(1)

        self.rubberBandExtent = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.setColor(Qt.red)
        self.rubberBandExtent.setWidth(2)

        self.isLayerVisible = True

        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self):
        self.startPoint = self.endPoint = self.firstPoint = None
        self.isEmittingPoint = False
        self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry)
        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()
        self.layer = None

    def deactivate(self):
        QgsMapToolEmitPoint.deactivate(self)
        self.reset()

    def canvasPressEvent(self, e):
        if self.firstPoint is None:
            self.startPoint = self.toMapCoordinates(e.pos())
            self.endPoint = self.startPoint
            self.isEmittingPoint = True
            self.originalCenter = self.layer.center
            # this tool do the displacement itself TODO update so it is done by
            # transformed coordinates + new center)
            self.originalCornerPoints = \
                self.layer.transformedCornerCoordinates(
                    *self.layer.transformParameters())

            self.isLayerVisible = isLayerVisible(self.iface,
                                                 self.layer)
            setLayerVisible(self.iface, self.layer, False)

            self.showDisplacement(self.startPoint, self.endPoint)
            self.layer.history.append({"action": "2pointsA", "center": self.layer.center})
        else:
            self.startPoint = self.toMapCoordinates(e.pos())
            self.endPoint = self.startPoint

            self.startY = e.pos().y()
            self.endY = self.startY
            self.isEmittingPoint = True
            self.height = self.canvas.height()

            self.isLayerVisible = isLayerVisible(self.iface,
                                                 self.layer)
            setLayerVisible(self.iface, self.layer, False)

            rotation = self.computeRotation()
            xScale = yScale = self.computeScale()
            self.showRotationScale(rotation, xScale, yScale)
            self.layer.history.append(
                {"action": "2pointsB", "center": self.layer.center, "xScale": self.layer.xScale,
                 "yScale": self.layer.yScale, "rotation": self.layer.rotation})

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False

        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()

        if self.firstPoint is None:
            x = (self.originalCenter.x() + self.endPoint.x() -
                 self.startPoint.x())
            y = (self.originalCenter.y() + self.endPoint.y() -
                 self.startPoint.y())
            self.layer.setCenter(QgsPointXY(x, y))
            self.firstPoint = self.endPoint

            setLayerVisible(self.iface, self.layer,
                            self.isLayerVisible)
            self.layer.repaint()

            self.layer.commitTransformParameters()
        else:
            rotation = self.computeRotation()
            xScale = yScale = self.computeScale()
            self.layer.moveCenterFromPointRotate(
                self.firstPoint, rotation, xScale, yScale)
            self.layer.setRotation(self.layer.rotation + rotation)
            self.layer.setScale(self.layer.xScale * xScale,
                                self.layer.yScale * yScale)

            setLayerVisible(self.iface, self.layer,
                            self.isLayerVisible)
            self.layer.repaint()

            self.layer.commitTransformParameters()

            self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
            self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
            self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry)
            self.rasterShadow.reset()

            self.firstPoint = None
            self.startPoint = self.endPoint = None

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())

        if self.firstPoint is None:
            self.showDisplacement(self.startPoint, self.endPoint)
        else:
            self.endY = e.pos().y()
            rotation = self.computeRotation()
            xScale = yScale = self.computeScale()
            self.showRotationScale(rotation, xScale, yScale)

    def computeRotation(self):
        # The angle is the difference between angle
        # horizontal/endPoint-firstPoint and horizontal/startPoint-firstPoint.
        dX0 = self.startPoint.x() - self.firstPoint.x()
        dY0 = self.startPoint.y() - self.firstPoint.y()
        dX = self.endPoint.x() - self.firstPoint.x()
        dY = self.endPoint.y() - self.firstPoint.y()
        return math.degrees(math.atan2(-dY, dX) - math.atan2(-dY0, dX0))

    def computeScale(self):
        # The scale is the ratio between endPoint-firstPoint and
        # startPoint-firstPoint.
        dX0 = self.startPoint.x() - self.firstPoint.x()
        dY0 = self.startPoint.y() - self.firstPoint.y()
        dX = self.endPoint.x() - self.firstPoint.x()
        dY = self.endPoint.y() - self.firstPoint.y()
        return math.sqrt((dX * dX + dY * dY) / (dX0 * dX0 + dY0 * dY0))

    def showRotationScale(self, rotation, xScale, yScale):
        center, _, _, _ = self.layer.transformParameters()
        # newRotation = rotation + originalRotation
        cornerPoints = self.layer.transformedCornerCoordinatesFromPoint(
            self.firstPoint, rotation, xScale, yScale)

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in cornerPoints:
            self.rubberBandExtent.addPoint(point, False)
        self.rubberBandExtent.addPoint(cornerPoints[0], True)
        self.rubberBandExtent.show()

        # Calculate the displacement of the center due to the rotation from
        # another point.
        newCenterDX = (cornerPoints[0].x() +
                       cornerPoints[2].x()) / 2 - center.x()
        newCenterDY = (cornerPoints[0].y() +
                       cornerPoints[2].y()) / 2 - center.y()
        self.rasterShadow.reset(self.layer)
        self.rasterShadow.setDeltaDisplacement(newCenterDX, newCenterDY, False)
        self.rasterShadow.setDeltaScale(xScale, yScale, False)
        self.rasterShadow.setDeltaRotation(rotation, True)
        self.rasterShadow.show()

        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        point0 = QgsPointXY(self.startPoint.x(), self.startPoint.y())
        point1 = QgsPointXY(self.firstPoint.x(), self.firstPoint.y())
        point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y())
        self.rubberBandDisplacement.addPoint(point0, False)
        self.rubberBandDisplacement.addPoint(point1, False)
        self.rubberBandDisplacement.addPoint(
            point2, True)  # true to update canvas
        self.rubberBandDisplacement.show()

    def showDisplacement(self, startPoint, endPoint):
        self.rubberBandOrigin.reset(QgsWkbTypes.PointGeometry)
        self.rubberBandOrigin.addPoint(endPoint, True)
        self.rubberBandOrigin.show()

        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(endPoint.x(), endPoint.y())
        self.rubberBandDisplacement.addPoint(point1, False)
        self.rubberBandDisplacement.addPoint(
            point2, True)  # true to update canvas
        self.rubberBandDisplacement.show()

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in self.originalCornerPoints:
            self._addDisplacementToPoint(self.rubberBandExtent, point, False)
        # for closing
        self._addDisplacementToPoint(
            self.rubberBandExtent, self.originalCornerPoints[0], True)
        self.rubberBandExtent.show()

        self.rasterShadow.reset(self.layer)
        self.rasterShadow.setDeltaDisplacement(self.endPoint.x(
        ) - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True)
        self.rasterShadow.show()

    def _addDisplacementToPoint(self, rubberBand, point, doUpdate):
        x = point.x() + self.endPoint.x() - self.startPoint.x()
        y = point.y() + self.endPoint.y() - self.startPoint.y()
        self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
Example #9
0
class SelectDownloadExtentMapTool(QgsMapTool):
    def __init__(self, canvas, dialog):
        self.canvas = canvas
        self.dialog = dialog
        QgsMapTool.__init__(self, self.canvas)
        self.setCursor(Qt.CrossCursor)
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.rubberBand.setColor(Qt.red)
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Polygon)

    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
        self.dialog.setExtent(self.rectangle())
        self.dialog.showNormal()
        self.dialog.raise_()
        self.dialog.activateWindow()
        self.reset()

    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(QGis.Polygon)
        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, 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)
class RectangleMapTool(QgsMapToolEmitPoint):
    """
    Map tool that lets the user define the analysis extents.
    """
    rectangle_created = pyqtSignal()
    deactivated = pyqtSignal()

    def __init__(self, canvas):
        """Constructor for the map tool.

        :param canvas: Canvas that tool will interact with.
        :type canvas: QgsMapCanvas
        """
        self.canvas = canvas
        self.start_point = None
        self.end_point = None
        self.is_emitting_point = False

        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubber_band = QgsRubberBand(self.canvas, geometryType=QGis.Line)
        self.rubber_band.setColor(QColor(0, 0, 240, 100))
        # Needs QGIS 2.6
        # self.rubber_band.setFillColor(QColor(0, 0, 240, 0))
        self.rubber_band.setWidth(1)

        self.reset()

    def reset(self):
        """
        Clear the rubber band for the analysis extents.
        """
        self.start_point = self.end_point = None
        self.is_emitting_point = False
        self.rubber_band.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        """
        Handle canvas press events so we know when user is capturing the rect.

        :param e: A Qt event object.
        :type: QEvent
        """
        self.start_point = self.toMapCoordinates(e.pos())
        self.end_point = self.start_point
        self.is_emitting_point = True

        self.show_rectangle(self.start_point, self.end_point)

    def canvasReleaseEvent(self, e):
        """
        Handle canvas release events  has finished capturing e

        :param e: A Qt event object.
        :type: QEvent
        """
        _ = e
        self.is_emitting_point = False
        self.rectangle_created.emit()

    def canvasMoveEvent(self, e):
        """

        :param e:
        :return:
        """
        if not self.is_emitting_point:
            return

        self.end_point = self.toMapCoordinates(e.pos())
        self.show_rectangle(self.start_point, self.end_point)

    def show_rectangle(self, start_point, end_point):
        """
        Show the rectangle on the canvas.

        :param start_point: QGIS Point object representing the origin (
            top left).
        :type start_point: QgsPoint

        :param end_point: QGIS Point object representing the contra-origin (
            bottom right).
        :type end_point: QgsPoint

        :return:
        """
        self.rubber_band.reset(QGis.Polygon)
        if (start_point.x() == end_point.x() or
                start_point.y() == end_point.y()):
            return

        point1 = start_point
        point2 = QgsPoint(end_point.x(), start_point.y())
        point3 = end_point
        point4 = QgsPoint(start_point.x(), end_point.y())

        update_canvas = False
        self.rubber_band.addPoint(point1, update_canvas)
        self.rubber_band.addPoint(point2, update_canvas)
        self.rubber_band.addPoint(point3, update_canvas)
        self.rubber_band.addPoint(point4, update_canvas)
        # noinspection PyArgumentEqualDefault
        # no False so canvas will update
        # close the polygon otherwise it shows as a filled rect
        self.rubber_band.addPoint(point1)
        self.rubber_band.show()

    def rectangle(self):
        """
        Accessor for the rectangle.

        :return: A rectangle showing the designed extent.
        :rtype: QgsRectangle
        """
        if self.start_point is None or self.end_point is None:
            return None
        elif self.start_point.x() == self.end_point.x() or \
                self.start_point.y() == self.end_point.y():
            return None

        return QgsRectangle(self.start_point, self.end_point)

    def set_rectangle(self, rectangle):
        """
        Set the rectangle for the selection.
        :param rectangle:
        :return:
        """
        if rectangle == self.rectangle():
            return False

        if rectangle is None:
            self.reset()
        else:
            self.start_point = QgsPoint(
                rectangle.xMinimum(), rectangle.yMinimum())
            self.end_point = QgsPoint(
                rectangle.xMaximum(), rectangle.yMaximum())
            self.show_rectangle(self.start_point, self.end_point)
        return True

    def deactivate(self):
        """
        Disable the tool.
        """
        QgsMapTool.deactivate(self)
        self.deactivated.emit()
Example #11
0
class MultiLayerSelection(QgsMapTool):
    finished = QtCore.pyqtSignal(list)
    def __init__(self, canvas, iface):
        """
        Tool Behaviours: (all behaviours start edition, except for rectangle one)
        1- Left Click: Clears previous selection, selects feature, sets feature layer as active layer. 
        The selection is done with the following priority: Point, Line then Polygon. 
        Selection is only done in visible layer.
        2- Control + Left Click: Adds to selection selected feature. This selection follows the priority in item 1.
        3- Right Click: Opens feature form
        4- Control + Right Click: clears selection and set feature's layer as activeLayer. activeLayer's definition
        follows priority of item 1;
        5- Shift + drag and drop: draws a rectangle, then features that intersect this rectangl'e are added to selection
        """
        self.iface = iface        
        self.canvas = canvas
        self.toolAction = None
        QgsMapTool.__init__(self, self.canvas)
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.hoverRubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        mFillColor = QColor( 254, 178, 76, 63 )
        self.rubberBand.setColor(mFillColor)
        self.hoverRubberBand.setColor(QColor( 255, 0, 0, 90 ))
        self.rubberBand.setWidth(1)
        self.reset()
        self.blackList = self.getBlackList()
        self.cursorChanged = False
        self.cursorChangingHotkey = QtCore.Qt.Key_Alt
        self.menuHovered = False # indicates hovering actions over context menu
    
    def keyPressEvent(self, e):
        """
        Reimplemetation of keyPressEvent() in order to handle cursor changing hotkey (Alt).
        """
        if e.key() == self.cursorChangingHotkey and not self.cursorChanged:
            self.cursorChanged = True
            QtGui.QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor))
        else:
            self.cursorChanged = False
            QtGui.QApplication.restoreOverrideCursor()
    
    def getBlackList(self):
        settings = QSettings()
        settings.beginGroup('PythonPlugins/DsgTools/Options')
        valueList = settings.value('valueList')
        if valueList:
            valueList = valueList.split(';')
            return valueList
        else:
            return ['moldura']
    
    def reset(self):
        """
        Resets rubber band.
        """
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Polygon)
    
    def keyPressEvent(self, e):
        """
        Reimplemetation of keyPressEvent() in order to handle cursor changing hotkey (F2).
        """
        if e.key() == self.cursorChangingHotkey and not self.cursorChanged:
            self.cursorChanged = True
            QtGui.QApplication.setOverrideCursor(QCursor(Qt.PointingHandCursor))
        else:
            self.cursorChanged = False
            QtGui.QApplication.restoreOverrideCursor()         

    def canvasMoveEvent(self, e):
        """
        Used only on rectangle select.
        """
        if self.menuHovered:
            # deactivates rubberband when the context menu is "destroyed" 
            self.hoverRubberBand.reset(QGis.Polygon)
        if not self.isEmittingPoint:
            return
        self.endPoint = self.toMapCoordinates( e.pos() )
        self.showRect(self.startPoint, self.endPoint)        

    def showRect(self, startPoint, endPoint):
        """
        Builds rubberband rect.
        """
        self.rubberBand.reset(QGis.Polygon)
        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, True)    # true to update canvas
        self.rubberBand.show()

    def rectangle(self):
        """
        Builds rectangle from self.startPoint and self.endPoint
        """
        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 setAction(self, action):
        self.toolAction = action
        self.toolAction.setCheckable(True)
    
    def canvasReleaseEvent(self, e):
        """
        After the rectangle is built, here features are selected.
        """
        if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
            firstGeom = self.checkSelectedLayers()
            self.isEmittingPoint = False
            r = self.rectangle()
            if r is None:
                return
            layers = self.canvas.layers()
            for layer in layers:
                #ignore layers on black list and features that are not vector layers
                if not isinstance(layer, QgsVectorLayer) or (self.layerHasPartInBlackList(layer.name())):
                    continue
                if firstGeom is not None and layer.geometryType() != firstGeom:
                    # if there are features already selected, shift will only get the same type geometry
                    # if more than one ty of geometry is present, only the strongest will be selected
                    continue
                #builds bbRect and select from layer, adding selection
                bbRect = self.canvas.mapSettings().mapToLayerCoordinates(layer, r)
                layer.select(bbRect, True)
            self.rubberBand.hide()

    def canvasPressEvent(self, e):
        """
        Method used to build rectangle if shift is held, otherwise, feature select/deselect and identify is done.
        """
        if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
            self.isEmittingPoint = True
            self.startPoint = self.toMapCoordinates(e.pos())
            self.endPoint = self.startPoint
            self.isEmittingPoint = True
            self.showRect(self.startPoint, self.endPoint)
        else:
            self.isEmittingPoint = False
            self.createContextMenu(e)
    
    def getCursorRect(self, e):
        """
        Calculates small cursor rectangle around mouse position. Used to facilitate operations
        """
        p = self.toMapCoordinates(e.pos())
        w = self.canvas.mapUnitsPerPixel() * 10
        return QgsRectangle(p.x()-w, p.y()-w, p.x()+w, p.y()+w)
    
    def layerHasPartInBlackList(self, lyrName):
        """
        Verifies if terms in black list appear on lyrName
        """
        for item in self.getBlackList():
            if item.lower() in lyrName.lower():
                return True
        return False
    
    def getPrimitiveDict(self, e, hasControlModifier=False):
        """
        Builds a dict with keys as geometryTypes of layer, which are QGis.Point (value 0), QGis.Line (value 1) or QGis.Polygon (value 2),
        and values as layers from self.iface.legendInterface().layers(). When self.iface.legendInterface().layers() is called, a list of
        layers ordered according to lyr order in TOC is returned.
        """
        #these layers are ordered by view order
        primitiveDict = dict()
        firstGeom = self.checkSelectedLayers()
        for lyr in self.iface.legendInterface().layers(): #ordered layers
            #layer types other than VectorLayer are ignored, as well as layers in black list and layers that are not visible
            if (lyr.type() != QgsMapLayer.VectorLayer) or (self.layerHasPartInBlackList(lyr.name())) or not self.iface.legendInterface().isLayerVisible(lyr):
                continue
            if hasControlModifier and (not firstGeom) and (not primitiveDict.keys() or lyr.geometryType() < firstGeom):
                firstGeom = lyr.geometryType()
            geomType = lyr.geometryType()
            if geomType not in primitiveDict.keys():
                primitiveDict[geomType] = []
            #removes selection
            if (not hasControlModifier and e.button() == QtCore.Qt.LeftButton) or (hasControlModifier and e.button() == QtCore.Qt.RightButton):
                lyr.removeSelection()
            primitiveDict[geomType].append(lyr)
        if hasControlModifier and firstGeom in [0, 1, 2]:
            return { firstGeom : primitiveDict[firstGeom] }
        else:
            return primitiveDict

    def deactivate(self):
        """
        Deactivate tool.
        """
        QtGui.QApplication.restoreOverrideCursor()
        self.hoverRubberBand.reset(QGis.Polygon)
        try:
            if self.toolAction:
                self.toolAction.setChecked(False)
            if self is not None:
                QgsMapTool.deactivate(self)
        except:
            pass

    def activate(self):
        """
        Activate tool.
        """
        if self.toolAction:
            self.toolAction.setChecked(True)
        QgsMapTool.activate(self)

    def setSelectionFeature(self, layer, feature, selectAll=False, setActiveLayer=False):
        """
        Selects a given feature on canvas. 
        :param layer: (QgsVectorLayer) layer containing the target feature.
        :param feature: (QgsFeature) taget feature to be selected.
        :param selectAll: (bool) indicates whether or not this fuction was called from a select all command.
                          so it doesn't remove selection from those that are selected already from the list.
        :param setActiveLayer: (bool) indicates whether method should set layer as active.
        """        
        idList = layer.selectedFeaturesIds()
        # self.iface.setActiveLayer(layer) # esp acho q o problema eh aqui
        # layer.startEditing()
        featId = feature.id()
        if featId not in idList:
            idList.append(featId)
        elif not selectAll:
            idList.pop(idList.index(featId))
        layer.setSelectedFeatures(idList)
        if setActiveLayer:
            layer.startEditing()
            if self.iface.activeLayer() != layer:
                self.iface.setActiveLayer(layer)
        return 

    def setSelectionListFeature(self, dictLayerFeature, selectAll=True):
        """
        Selects all features on canvas of a given dict.        
        :param dictLayerFeature: (dict) dict of layers/features to be selected.
        :param selectAll: (bool) indicates if "All"-command comes from a "Select All". In that case, selected features
                          won't be deselected.
        """
        for layer in dictLayerFeature.keys():
            geomType = layer.geometryType()
            # ID list of features already selected
            idList = layer.selectedFeaturesIds()
            # restart feature ID list for each layer
            featIdList = []
            for feature in dictLayerFeature[layer]:
                featId = feature.id()
                if featId not in idList:
                    idList.append(featId)
                elif not selectAll:
                    idList.pop(idList.index(featId))
            layer.setSelectedFeatures(idList)
            layer.startEditing()
        # last layer is set active and 
        if self.iface.activeLayer() != layer:
            self.iface.setActiveLayer(layer)

    def openMultipleFeatureForm(self, dictLayerFeature):
        """
        Opens all features Feature Forms of a given list.
        :param dictLayerFeature: (dict) dict of layers/features to have their feature form exposed.
        """
        for layer, features in dictLayerFeature.iteritems():
            for feat in features:
                self.iface.openFeatureForm(layer, feat, showModal=False)

    def filterStrongestGeometry(self, dictLayerFeature):
        """
        Filter a given dict of features for its strongest geometry.
        :param dictLayerFeature: (dict) a dict of layers and its features to be filtered.
        :return: (dict) filtered dict with only layers of the strongest geometry on original dict.
        """
        strongest_geometry = 3
        outDict = dict()
        if dictLayerFeature:
            for lyr in dictLayerFeature.keys():
                # to retrieve strongest geometry value
                if strongest_geometry > lyr.geometryType():
                    strongest_geometry = lyr.geometryType()
                if strongest_geometry == 0:
                    break
        for lyr in dictLayerFeature.keys():
            if lyr.geometryType() == strongest_geometry:
                outDict[lyr] = dictLayerFeature[lyr]
        return outDict
    
    def createRubberBand(self, feature, layer, geom):
        """
        Creates a rubber band around from a given a standard feature string.
        :param feature: taget feature to be highlighted 
        :param layer: layer containing the target feature
        :param geom: int indicating geometry type of target feature
        """
        if geom == 0:
            self.hoverRubberBand.reset(QGis.Point)
        elif geom == 1:
            self.hoverRubberBand.reset(QGis.Line)
        else:
            self.hoverRubberBand.reset(QGis.Polygon)
        self.hoverRubberBand.addGeometry(feature.geometry(), layer)
        # to inform the code that menu has been hovered over
        self.menuHovered = True

    def createMultipleRubberBand(self, dictLayerFeature):
        """
        Creates rubberbands around features.
        :param dictLayerFeature: (dict) dict of layer/features to have rubberbands built around.
        """
        # only one type of geometry at a time will have rubberbands around it
        geom = dictLayerFeature.keys()[0].geometryType()
        if geom == 0:
            self.hoverRubberBand.reset(QGis.Point)
        elif geom == 1:
            self.hoverRubberBand.reset(QGis.Line)
        else:
            self.hoverRubberBand.reset(QGis.Polygon)
        for layer, features in dictLayerFeature.iteritems():
            for feat in features:
                self.hoverRubberBand.addGeometry(feat.geometry(), layer)
        self.menuHovered = True

    def checkSelectedLayers(self):
        """
        Checks if there are layers selected on canvas. If there are, returns the geometry type of
        selected feature(s). If more than one type of feature is selected, the "strongest" geometry
        is returned.
        """
        geom = None
        for layer in self.iface.legendInterface().layers():
            if isinstance(layer, QgsVectorLayer):
                selection = layer.selectedFeatures()
                if len(selection):
                    if geom == None:
                        geom = layer.geometryType()
                        continue
                    elif layer.geometryType() < geom:
                        geom = layer.geometryType()
                        continue
        return geom
    
    def addCallBackToAction(self, action, onTriggeredAction, onHoveredAction=None):
        """
        Adds action the command to the action. If onHoveredAction is given, signal "hovered" is applied with given action.
        :param action: (QAction) associated with target context menu.
        :param onTriggeredAction: (object) action to be executed when the given action is triggered.
        :param onHoveredAction: (object) action to be executed whilst the given action is hovered.
        """
        action.triggered[()].connect(onTriggeredAction)
        if onHoveredAction:
            action.hovered[()].connect(onHoveredAction)

    def getCallback(self, e, layer, feature, geomType=None, selectAll=True):
        """
        Gets the callback for an action.
        :param e: (QMouseEvent) mouse event on canvas.
        :param layer: (QgsVectorLayer) layer to be treated.
        :param feature: (QgsFeature) feature to be treated.
        :param geomType: (int) code indicating layer geometry type. It is retrieved OTF in case it's not given.
        :return: (tuple-of function_lambda) callbacks for triggered and hovered signals.
        """
        if not geomType:
            geomType = layer.geometryType()
        if e.button() == QtCore.Qt.LeftButton: 
            # line added to make sure the action is associated with current loop value,
            # lambda function is used with standard parameter set to current loops value.
            triggeredAction = lambda t=[layer, feature] : self.setSelectionFeature(t[0], feature=t[1], selectAll=selectAll, setActiveLayer=True)
            hoveredAction = lambda t=[layer, feature] : self.createRubberBand(feature=t[1], layer=t[0], geom=geomType)
        elif e.button() == QtCore.Qt.RightButton:
            selected = (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier)
            if selected:
                triggeredAction = lambda layer=layer : self.iface.setActiveLayer(layer)
                hoveredAction = None
            else:
                triggeredAction = lambda t=[layer, feature] : self.iface.openFeatureForm(t[0], t[1], showModal=False)
                hoveredAction = lambda t=[layer, feature] : self.createRubberBand(feature=t[1], layer=t[0], geom=geomType)
        return triggeredAction, hoveredAction

    def getCallbackMultipleFeatures(self, e, dictLayerFeature, selectAll=True):
        """
        Sets the callback of an action with a list features as target.
        :param e: (QMouseEvent) mouse event on canvas.
        :param dictLayerFeature: (dict) dictionary containing layers/features to be treated.
        :return: (tuple-of function_lambda) callbacks for triggered and hovered signals.
        """
        # setting the action for the "All" options
        if e.button() == QtCore.Qt.LeftButton:
            triggeredAction = lambda t=dictLayerFeature: self.setSelectionListFeature(dictLayerFeature=t, selectAll=selectAll)
        else:
            triggeredAction = lambda t=dictLayerFeature: self.openMultipleFeatureForm(dictLayerFeature=t)
        # to trigger "Hover" signal on QMenu for the multiple options
        hoveredAction = lambda t=dictLayerFeature : self.createMultipleRubberBand(dictLayerFeature=t)
        return triggeredAction, hoveredAction

    def createSubmenu(self, e, parentMenu, menuDict, genericAction, selectAll):
        """
        Creates a submenu in a given parent context menu and populates it, with classes/feature sublevels from the menuDict. 
        :param e: (QMouseEvent) mouse event on canvas. If menuDict has only 1 class in it, method will populate parent QMenu.
        :param parentMenu: (QMenu) menu containing the populated submenu
        :param menuDict: (dict) dictionary containing all classes and their features to be filled into submenu.
        :param genericAction: (str) text to be shown into generic action description on the outter level of submenu.
        :return: (dict) mapping of classes and their own QMenu object.
        """
        # creating a dict to handle all "menu" for each class
        submenuDict = dict()
        # sort the layers from diciotnary
        classNameDict = { cl.name() : cl for cl in menuDict.keys() }
        layers = sorted(classNameDict.keys())
        for className in layers:
            # menu for features of each class
            cl = classNameDict[className]
            geomType = cl.geometryType()
            # get layer database name
            dsUri = cl.dataProvider().dataSourceUri()
            temp = []
            if '/' in dsUri or '\\' in dsUri:
                db_name = dsUri.split("|")[0] if "|" in dsUri else dsUri
                # data source is a file, not a postgres database
                dbIsFile = True
            elif 'memory' in dsUri:
                db_name = self.tr(u'{0} (Memory Layer)').format(className)
                dbIsFile = True
            else:
                db_name = dsUri.split("'")[1]
                dbIsFile = False
            if len(menuDict) == 1:
                # if dictionaty has only 1 class, no need for an extra QMenu - features will be enlisted directly
                # order features by ID to be displayer ordered
                featDict = { feat.id() : feat for feat in  menuDict[cl] }
                orderedFeatIdList = sorted(featDict.keys())
                for featId in orderedFeatIdList:
                    feat = featDict[featId]
                    if dbIsFile:
                        s = u'{0} (feat_id = {1})'.format(db_name, featId)
                    else:
                        s = u'{0}.{1} (feat_id = {2})'.format(db_name, className, featId)
                    # inserting action for each feature
                    action = parentMenu.addAction(s)
                    triggeredAction, hoveredAction = self.getCallback(e=e, layer=cl, feature=feat, geomType=geomType, selectAll=selectAll)
                    self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
                # inserting generic action, if necessary
                if len(menuDict[cl]) > 1:
                    # if there are more than 1 feature to be filled, "All"-command should be added
                    action = parentMenu.addAction(self.tr(u"{0} From Class {1}").format(genericAction, className))
                    triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=menuDict, selectAll=selectAll)
                    self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
                # there is no mapping of class to be exposed, only information added to parent QMenu itself
                return dict()
            if dbIsFile:
                title = db_name
            else:
                title = '{0}.{1}'.format(db_name, className)
            submenuDict[cl] = QtGui.QMenu(title=title, parent=parentMenu)
            parentMenu.addMenu(submenuDict[cl])
            # inserting an entry for every feature of each class in its own context menu
            # order features by ID to be displayer ordered
            featDict = { feat.id() : feat for feat in  menuDict[cl] }
            orderedFeatIdList = sorted(featDict.keys())
            for featId in orderedFeatIdList:
                feat = featDict[featId]
                s = u'feat_id = {0}'.format(featId)
                action = submenuDict[cl].addAction(s)
                triggeredAction, hoveredAction = self.getCallback(e=e, layer=cl, feature=feat, geomType=geomType, selectAll=selectAll)
                self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
                # set up list for the "All"-commands
                temp.append([cl, feat, geomType])
            # adding generic action for each class
            if len(menuDict[cl]) > 1:
                # if there are more than 1 feature to be filled, "All"-command should be added
                action = submenuDict[cl].addAction(self.tr(u"{0} From Class {1}").format(genericAction, className))
                triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature={ cl : menuDict[cl] }, selectAll=selectAll)
                self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
        return submenuDict

    def setContextMenuStyle(self, e, dictMenuSelected, dictMenuNotSelected):
        """
        Defines how many "submenus" the context menu should have.
        There are 3 context menu scenarios to be handled:
        :param e: (QMouseEvent) mouse event on canvas.
        :param dictMenuSelected: (dict) dictionary of classes and its selected features being treatead.
        :param dictMenuNotSelected: (dict) dictionary of classes and its non selected features being treatead.
        """
        # finding out filling conditions
        selectedDict = bool(dictMenuSelected)
        notSelectedDict = bool(dictMenuNotSelected)
        # finding out if one of either dictionaty are filled ("Exclusive or")
        selectedXORnotSelected = (selectedDict != notSelectedDict)
        # setting "All"-command name
        if e.button() == QtCore.Qt.RightButton:
            genericAction = self.tr('Open All Feature Forms')
        else:
            genericAction = self.tr('Select All Features')
        # in case one of given dict is empty
        if selectedXORnotSelected:
            if selectedDict:
                menuDict, menu = dictMenuSelected, QtGui.QMenu(title=self.tr('Selected Features'))
                genericAction = self.tr('Deselect All Features')
                # if the dictionary is from selected features, we want commands to be able to deselect them
                selectAll = False
            else:
                menuDict, menu = dictMenuNotSelected, QtGui.QMenu(title=self.tr('Not Selected Features'))
                genericAction = self.tr('Select All Features')
                # if the dictionary is from non-selected features, we want commands to be able to select them
                selectAll = True
            if e.button() == QtCore.Qt.RightButton:
                genericAction = self.tr('Open All Feature Forms')
            self.createSubmenu(e=e, parentMenu=menu, menuDict=menuDict, genericAction=genericAction, selectAll=selectAll)
            if len(menuDict) != 1 and len(menuDict.values()) > 1:
                # if there's only one class, "All"-command is given by createSubmenu method
                action = menu.addAction(genericAction)
                triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=menuDict, selectAll=selectAll)
                self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
        elif selectedDict:
            # if both of them is empty one more QMenu level is added
            menu = QtGui.QMenu()
            selectedMenu = QtGui.QMenu(title=self.tr('Selected Features'))
            notSelectedMenu = QtGui.QMenu(title=self.tr('Not Selected Features'))
            menu.addMenu(selectedMenu)
            menu.addMenu(notSelectedMenu)
            selectedGenericAction = self.tr('Deselect All Features')
            notSelectedGenericAction = self.tr('Select All Features')
            # selectAll is set to True as now we want command to Deselect Features in case they are selected
            self.createSubmenu(e=e, parentMenu=selectedMenu, menuDict=dictMenuSelected, genericAction=selectedGenericAction, selectAll=False)
            if len(dictMenuSelected) != 1 and len(dictMenuSelected.values()) > 1:
                # if there's only one class, "All"-command is given by createSubmenu method
                action = selectedMenu.addAction(selectedGenericAction)
                triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=dictMenuSelected, selectAll=False)
                self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)
            self.createSubmenu(e=e, parentMenu=notSelectedMenu, menuDict=dictMenuNotSelected, genericAction=notSelectedGenericAction, selectAll=True)
            if len(dictMenuNotSelected) != 1 and len(dictMenuNotSelected.values()) > 1:
                # if there's only one class, "All"-command is given by createSubmenu method
                action = notSelectedMenu.addAction(notSelectedGenericAction)
                triggeredAction, hoveredAction = self.getCallbackMultipleFeatures(e=e, dictLayerFeature=dictMenuNotSelected, selectAll=True)
                self.addCallBackToAction(action=action, onTriggeredAction=triggeredAction, onHoveredAction=hoveredAction)

        menu.exec_(self.canvas.viewport().mapToGlobal(e.pos()))

    def checkSelectedFeaturesOnDict(self, menuDict):
        """
        Checks all selected features from a given dictionary ( { (QgsVectorLayer)layer : [ (QgsFeature)feat ] } ).
        :param menuDict: (dict) dictionary with layers and their features to be analyzed.
        :return: (tuple-of-dict) both dictionaries of selected and non-selected features of each layer.
        """
        selectedFeaturesDict, notSelectedFeaturesDict = dict(), dict()
        for cl in menuDict.keys():
            selectedFeats = [f.id() for f in cl.selectedFeatures()]
            for feat in menuDict[cl]:
                if feat.id() in selectedFeats:
                    if cl not in selectedFeaturesDict.keys():
                        selectedFeaturesDict[cl] = [feat]
                    else:
                        selectedFeaturesDict[cl].append(feat)
                else:
                    if cl not in notSelectedFeaturesDict.keys():
                        notSelectedFeaturesDict[cl] = [feat]
                    else:
                        notSelectedFeaturesDict[cl].append(feat)
        return selectedFeaturesDict, notSelectedFeaturesDict

    def reprojectSearchArea(self, layer, geom):
        """
        Reprojects search area if necessary, according to what is being searched.
        :param layer: (QgsVectorLayer) layer which target rectangle has to have same SRC.
        :param geom: (QgsRectangle) rectangle representing search area.
        """
        #geom always have canvas coordinates
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        #getting srid from something like 'EPSG:31983'
        srid = layer.crs().authid()
        if epsg == srid:
            return geom
        crsSrc = QgsCoordinateReferenceSystem(epsg)
        crsDest = QgsCoordinateReferenceSystem(srid)
        # Creating a transformer
        coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest) # here we have to put authid, not srid
        auxGeom = QgsGeometry.fromRect(geom)
        auxGeom.transform(coordinateTransformer)
        return auxGeom.boundingBox()

    def createContextMenu(self, e):
        """
        Creates the context menu for overlapping layers.
        :param e: mouse event caught from canvas.
        """
        selected = (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier)
        if selected:
            firstGeom = self.checkSelectedLayers()
        # setting a list of features to iterate over
        layerList = self.getPrimitiveDict(e, hasControlModifier=selected)
        layers = []
        for key in layerList.keys():
            layers += layerList[key]
        if layers:
            rect = self.getCursorRect(e)
            lyrFeatDict = dict()
            for layer in layers:
                if not isinstance(layer, QgsVectorLayer):
                    continue
                geomType = layer.geometryType()
                # iterate over features inside the mouse bounding box
                bbRect = self.canvas.mapSettings().mapToLayerCoordinates(layer, rect)
                for feature in layer.getFeatures(QgsFeatureRequest(bbRect)):
                    geom = feature.geometry()
                    if geom:
                        searchRect = self.reprojectSearchArea(layer, rect)
                        if selected:
                            # if Control was held, appending behaviour is different
                            if not firstGeom:
                                firstGeom = geomType
                            elif firstGeom > geomType:
                                firstGeom = geomType
                            if geomType == firstGeom and geom.intersects(searchRect):
                                # only appends features if it has the same geometry as first selected feature
                                if layer in lyrFeatDict.keys():
                                    lyrFeatDict[layer].append(feature)
                                else:
                                    lyrFeatDict[layer] = [feature]
                        else:
                            if geom.intersects(searchRect):
                                if layer in lyrFeatDict.keys():
                                    lyrFeatDict[layer].append(feature)
                                else:
                                    lyrFeatDict[layer] = [feature]
            lyrFeatDict = self.filterStrongestGeometry(lyrFeatDict)
            if lyrFeatDict:
                moreThanOneFeat = lyrFeatDict.values() and len(lyrFeatDict.values()) > 1 or len(lyrFeatDict.values()[0]) > 1
                if moreThanOneFeat:
                    # if there are overlapping features (valid candidates only)
                    selectedFeaturesDict, notSelectedFeaturesDict = self.checkSelectedFeaturesOnDict(menuDict=lyrFeatDict)
                    self.setContextMenuStyle(e=e, dictMenuSelected=selectedFeaturesDict, dictMenuNotSelected=notSelectedFeaturesDict)
                else:
                    layer = lyrFeatDict.keys()[0]
                    feature = lyrFeatDict[layer][0]
                    selected =  (QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier)
                    if e.button() == QtCore.Qt.LeftButton:
                        # if feature is selected, we want it to be de-selected
                        self.setSelectionFeature(layer=layer, feature=feature, selectAll=False, setActiveLayer=True)
                    elif selected:
                        if self.iface.activeLayer() != layer:
                            self.iface.setActiveLayer(layer)
                    else:
                        self.iface.openFeatureForm(layer, feature, showModal=False)
class ApisMapToolEmitPolygonAndPoint(QgsMapTool, ApisMapToolMixin):

    mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem)

    def __init__(self, canvas):
        QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.rubberBand = None
        self.tempRubberBand = None
        self.vertexMarker = None
        self.capturedPoints = []
        self.derivedPoint = None
        self.capturing = False
        self.setCursor(Qt.CrossCursor)

    def canvasReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if not self.capturing:
                self.startCapturing()
            self.addVertex(event.pos())
        elif event.button() == Qt.RightButton:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point != None and polygon != None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom != None and polygonGeom != None:
                    self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def canvasMoveEvent(self, event):
        if self.tempRubberBand != None and self.capturing:
            mapPt = self.transformCoordinates(event.pos())
            self.tempRubberBand.movePoint(mapPt)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete:
            self.removeLastVertex()
            event.ignore()
        if event.key() == Qt.Key_Escape:
            self.stopCapturing()
            self.clearScene()
        if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
            point = self.getDerivedPoint()
            polygon = self.getCapturedPolygon()
            self.stopCapturing()
            if point != None and polygon != None:
                pointGeom = self.getPointGeometry(point)
                polygonGeom = self.getPolygonGeometry(polygon)
                if pointGeom != None and polygonGeom != None:
                    self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs())
                else:
                    self.clearScene()
            else:
                self.clearScene()

    def startCapturing(self):
        self.clearScene()

        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.rubberBand.setWidth(2)
        self.rubberBand.setFillColor(QColor(220, 0, 0, 120))
        self.rubberBand.setBorderColor(QColor(220, 0, 0))
        self.rubberBand.setLineStyle(Qt.DotLine)
        self.rubberBand.show()

        self.tempRubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.tempRubberBand.setWidth(2)
        self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0))
        self.tempRubberBand.setBorderColor(QColor(220, 0, 0))
        self.tempRubberBand.setLineStyle(Qt.DotLine)
        self.tempRubberBand.show()

        self.vertexMarker = QgsVertexMarker(self.canvas)
        self.vertexMarker.setIconType(1)
        self.vertexMarker.setColor(QColor(220, 0, 0))
        self.vertexMarker.setIconSize(16)
        self.vertexMarker.setPenWidth(3)
        self.vertexMarker.show()

        self.capturing = True

    def clearScene(self):
        if self.vertexMarker:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None

    def stopCapturing(self):
        if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices() < 3:
            self.canvas.scene().removeItem(self.vertexMarker)
            self.vertexMarker = None
        if self.rubberBand and self.rubberBand.numberOfVertices() < 3:
            self.canvas.scene().removeItem(self.rubberBand)
            self.rubberBand = None
        if self.tempRubberBand:
            self.canvas.scene().removeItem(self.tempRubberBand)
            self.tempRubberBand = None
        self.capturing = False
        self.capturedPoints = []
        self.derivedPoint = None
        self.canvas.refresh()

    def addVertex(self, canvasPoint):
        mapPt = self.transformCoordinates(canvasPoint)

        self.rubberBand.addPoint(mapPt)
        self.capturedPoints.append(mapPt)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize > 2:

            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()



        self.tempRubberBand.reset(QGis.Polygon)
        firstPoint = self.rubberBand.getPoint(0, 0)
        self.tempRubberBand.addPoint(firstPoint)
        self.tempRubberBand.movePoint(mapPt)
        self.tempRubberBand.addPoint(mapPt)

    def removeLastVertex(self):
        if not self.capturing:
            return

        bandSize = self.rubberBand.numberOfVertices()
        tempBandSize = self.tempRubberBand.numberOfVertices()
        numPoints = len(self.capturedPoints)

        if bandSize < 1 or numPoints < 1:
            return

        self.rubberBand.removePoint(-1)

        if bandSize > 1:
            if tempBandSize > 1:
                point = self.rubberBand.getPoint(0, bandSize - 2)
                self.tempRubberBand.movePoint(tempBandSize - 2, point)
        else:
            self.tempRubberBand.reset(QGis.Polygon)

        bandSize = self.rubberBand.numberOfVertices()
        if bandSize < 3:
            self.vertexMarker.hide()
        else:
            rubGeom = self.rubberBand.asGeometry()
            cpGeom = rubGeom.centroid()
            if not rubGeom.contains(cpGeom):
                cpGeom = rubGeom.pointOnSurface()
            #nearestCp = rubGeom.nearestPoint(cpGeom)
            self.vertexMarker.setCenter(cpGeom.asPoint())
            self.derivedPoint = cpGeom.asPoint()
            self.vertexMarker.show()


        del self.capturedPoints[-1]

    def getCapturedPolygon(self):
        polygon = self.capturedPoints

        if len(polygon) < 3:
            return None
        else:
            return polygon

    def getDerivedPoint(self):
        point = self.derivedPoint

        if point == None:
            return None
        else:
            return point

    def getPointGeometry(self, geom):
        p = QgsGeometry.fromPoint(geom)
        if p.isGeosValid() and not p.isGeosEmpty():
            return p
        else:
            return None

    def getPolygonGeometry(self, geom):
        p = QgsGeometry.fromPolygon([geom])
        if p.isGeosValid() and not p.isGeosEmpty():
            return p
        else:
            return None
Example #13
0
class ArkMapToolInteractive(QgsMapTool):

    _active = False

    _dragging = False
    _panningEnabled = False

    _zoomingEnabled = False
    _zoomRubberBand = None  #QgsRubberBand()
    _zoomRect = None # QRect()

    _snappingEnabled = False
    _snapper = None  #QgsMapCanvasSnapper()
    _snappingMarker = None  # QgsVertexMarker()

    _showSnappableVertices = False
    _snappableVertices = []  # [QgsPoint()]
    _snappableMarkers = []  # [QgsVertexMarker()]

    def __init__(self, canvas, snappingEnabled=False, showSnappableVertices=False):
        super(ArkMapToolInteractive, self).__init__(canvas)
        self._snappingEnabled = snappingEnabled
        self._showSnappableVertices = showSnappableVertices

    def __del__(self):
        if self._active:
            self.deactivate()

    def isActive(self):
        return self._active

    def activate(self):
        super(ArkMapToolInteractive, self).activate()
        self._active = True
        self._startSnapping()

    def deactivate(self):
        self._active = False
        if self._snappingEnabled:
            self._stopSnapping()
        if (self._zoomRubberBand is not None):
            self.canvas().scene().removeItem(self._zoomRubberBand)
            self._zoomRubberBand = None
        super(ArkMapToolInteractive, self).deactivate()

    def setAction(self, action):
        super(ArkMapToolInteractive, self).setAction(action)
        self.action().triggered.connect(self._activate)

    def _activate(self):
        self.canvas().setMapTool(self)

    def panningEnabled(self):
        return self._panningEnabled

    def setPanningEnabled(self, enabled):
        self._panningEnabled = enabled

    def zoomingEnabled(self):
        return self._zoomingEnabled

    def setZoomingEnabled(self, enabled):
        self._zoomingEnabled = enabled

    def snappingEnabled(self):
        return self._snappingEnabled

    def setSnappingEnabled(self, enabled):
        if (self._snappingEnabled == enabled):
            return
        self._snappingEnabled = enabled
        if not self._active:
            return
        if enabled:
            self._startSnapping()
        else:
            self._stopSnapping()

    def _startSnapping(self):
        self._snapper = QgsMapCanvasSnapper()
        self._snapper.setMapCanvas(self.canvas())
        if self._showSnappableVertices:
            self._startSnappableVertices()

    def _stopSnapping(self):
        self._deleteSnappingMarker()
        self._snapper = None
        if self._showSnappableVertices:
            self._stopSnappableVertices()

    def showSnappableVertices(self):
        return self._showSnappableVertices

    def setShowSnappableVertices(self, show):
        if (self._showSnappableVertices == show):
            return
        self._showSnappableVertices = show
        if not self._active:
            return
        if show:
            self._startSnappableVertices()
        else:
            self._stopSnappableVertices()

    def _startSnappableVertices(self):
        self.canvas().layersChanged.connect(self._layersChanged)
        self.canvas().extentsChanged.connect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.connect(self._layersChanged)
        self._layersChanged()

    def _stopSnappableVertices(self):
        self._deleteSnappableMarkers()
        self._snappableLayers = []
        self.canvas().layersChanged.disconnect(self._layersChanged)
        self.canvas().extentsChanged.disconnect(self._redrawSnappableMarkers)
        QgsProject.instance().snapSettingsChanged.disconnect(self._layersChanged)

    def canvasMoveEvent(self, e):
        super(ArkMapToolInteractive, self).canvasMoveEvent(e)
        if not self._active:
            return
        e.ignore()
        if (self._panningEnabled and e.buttons() & Qt.LeftButton):
            # Pan map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
            self.canvas().panAction(e)
            e.accept()
        elif (self._zoomingEnabled and e.buttons() & Qt.RightButton):
            # Zoom map mode
            if not self._dragging:
                self._dragging = True
                self.setCursor(QCursor(Qt.ClosedHandCursor))
                self._zoomRubberBand = QgsRubberBand(self.canvas(), QGis.Polygon)
                color = QColor(Qt.blue)
                color.setAlpha(63)
                self._zoomRubberBand.setColor(color)
                self._zoomRect = QRect(0, 0, 0, 0)
                self._zoomRect.setTopLeft(e.pos())
            self._zoomRect.setBottomRight(e.pos())
            if self._zoomRubberBand is not None:
                self._zoomRubberBand.setToCanvasRectangle(self._zoomRect)
                self._zoomRubberBand.show()
            e.accept()
        elif self._snappingEnabled:
            mapPoint, snapped = self._snapCursorPoint(e.pos())
            if (snapped):
                self._createSnappingMarker(mapPoint)
            else:
                self._deleteSnappingMarker()

    def canvasReleaseEvent(self, e):
        super(ArkMapToolInteractive, self).canvasReleaseEvent(e)
        e.ignore()
        if (e.button() == Qt.LeftButton):
            if self._dragging:
                # Pan map mode
                self.canvas().panActionEnd(e.pos())
                self.setCursor(capture_point_cursor)
                self._dragging = False
                e.accept()
        elif (e.button() == Qt.RightButton):
            if self._dragging:
                # Zoom mode
                self._zoomRect.setBottomRight(e.pos())
                if (self._zoomRect.topLeft() != self._zoomRect.bottomRight()):
                    coordinateTransform = self.canvas().getCoordinateTransform()
                    ll = coordinateTransform.toMapCoordinates(self._zoomRect.left(), self._zoomRect.bottom())
                    ur = coordinateTransform.toMapCoordinates(self._zoomRect.right(), self._zoomRect.top())
                    r = QgsRectangle()
                    r.setXMinimum(ll.x())
                    r.setYMinimum(ll.y())
                    r.setXMaximum(ur.x())
                    r.setYMaximum(ur.y())
                    r.normalize()
                    if (r.width() != 0 and r.height() != 0):
                        self.canvas().setExtent(r)
                        self.canvas().refresh()
                self._dragging = False
                if (self._zoomRubberBand is not None):
                    self.canvas().scene().removeItem(self._zoomRubberBand)
                    self._zoomRubberBand = None
                e.accept()

    def keyPressEvent(self, e):
        super(ArkMapToolInteractive, self).keyPressEvent(e)
        if (e.key() == Qt.Key_Escape):
            self.canvas().unsetMapTool(self)
            e.accept()

    def _snapCursorPoint(self, cursorPoint):
        res, snapResults = self._snapper.snapToBackgroundLayers(cursorPoint)
        if (res != 0 or len(snapResults) < 1):
            return self.toMapCoordinates(cursorPoint), False
        else:
            # Take a copy as QGIS will delete the result!
            snappedVertex = QgsPoint(snapResults[0].snappedVertex)
            return snappedVertex, True

    def _createSnappingMarker(self, snapPoint):
        if (self._snappingMarker is None):
            self._snappingMarker = QgsVertexMarker(self.canvas())
            self._snappingMarker.setIconType(QgsVertexMarker.ICON_CROSS)
            self._snappingMarker.setColor(Qt.magenta)
            self._snappingMarker.setPenWidth(3)
        self._snappingMarker.setCenter(snapPoint)

    def _deleteSnappingMarker(self):
        if (self._snappingMarker is not None):
            self.canvas().scene().removeItem(self._snappingMarker)
            self._snappingMarker = None

    def _createSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        extent = self.canvas().extent()
        for vertex in self._snappableVertices.asMultiPoint():
            if (extent.contains(vertex)):
                marker = QgsVertexMarker(self.canvas())
                marker.setIconType(QgsVertexMarker.ICON_X)
                marker.setColor(Qt.gray)
                marker.setPenWidth(1)
                marker.setCenter(vertex)
                self._snappableMarkers.append(marker)

    def _deleteSnappableMarkers(self):
        for marker in self._snappableMarkers:
            self.canvas().scene().removeItem(marker)
        del self._snappableMarkers[:]

    def _layersChanged(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._buildSnappableLayers()
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _redrawSnappableMarkers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        self._deleteSnappableMarkers()
        self._createSnappableMarkers()

    def _buildSnappableLayers(self):
        if (not self._showSnappableVertices or not self._snappingEnabled):
            return
        vertices = []
        for layer in self.canvas().layers():
            ok, enabled, type, units, tolerance, avoid = QgsProject.instance().snapSettingsForLayer(layer.id())
            if (ok and enabled and not layer.isEditable()):
                for feature in layer.getFeatures():
                    geometry = feature.geometry()
                    if geometry is None:
                        pass
                    elif geometry.type() == QGis.Point:
                        vertices.extend([geometry.asPoint()])
                    elif geometry.type() == QGis.Line:
                        vertices.extend(geometry.asPolyline())
                    elif geometry.type() == QGis.Polygon:
                        lines = geometry.asPolygon()
                        for line in lines:
                            vertices.extend(line)
        self._snappableVertices = QgsGeometry.fromMultiPoint(vertices)
        self._snappableVertices.simplify(0)
Example #14
0
class RectangleMapTool(QgsMapToolEmitPoint):

  def __init__(self, canvas):
    QgsMapToolEmitPoint.__init__(self, canvas)

    self.canvas = canvas
    self.rubberBand = QgsRubberBand(canvas, QGis.Polygon)
    self.rubberBand.setColor(QColor(255, 0, 0, 180))
    self.rubberBand.setWidth(1)
    self.reset()

  def reset(self):
    self.startPoint = self.endPoint = None
    self.isDrawing = False
    self.rubberBand.reset(QGis.Polygon)

  def canvasPressEvent(self, e):
    self.startPoint = self.toMapCoordinates(e.pos())
    self.endPoint = self.startPoint

    mapSettings = self.canvas.mapSettings() if QGis.QGIS_VERSION_INT >= 20300 else self.canvas.mapRenderer()
    self.mupp = mapSettings.mapUnitsPerPixel()
    self.rotation = mapSettings.rotation() if QGis.QGIS_VERSION_INT >= 20700 else 0

    self.isDrawing = True
    self.showRect(self.startPoint, self.endPoint)

  def canvasReleaseEvent(self, e):
    self.isDrawing = False
    self.emit(SIGNAL("rectangleCreated()"))

  def canvasMoveEvent(self, e):
    if not self.isDrawing:
      return
    self.endPoint = self.toMapCoordinates(e.pos())
    self.showRect(self.startPoint, self.endPoint)

  def showRect(self, startPoint, endPoint):
    self.rubberBand.reset(QGis.Polygon)
    if startPoint.x() == endPoint.x() and startPoint.y() == endPoint.y():
      return

    for i, pt in enumerate(self._rect(startPoint, endPoint).vertices()):
      self.rubberBand.addPoint(pt, bool(i == 3))
    self.rubberBand.show()

  def _rect(self, startPoint, endPoint):
    if startPoint is None or endPoint is None:
      return None

    p0 = self.toCanvasCoordinates(startPoint)
    p1 = self.toCanvasCoordinates(endPoint)
    canvas_rect = QgsRectangle(QgsPoint(p0.x(), p0.y()), QgsPoint(p1.x(), p1.y()))
    center = QgsPoint((startPoint.x() + endPoint.x()) / 2, (startPoint.y() + endPoint.y()) / 2)
    return RotatedRect(center, self.mupp * canvas_rect.width(), self.mupp * canvas_rect.height()).rotate(self.rotation, center)

  def rectangle(self):
    return self._rect(self.startPoint, self.endPoint)

  def setRectangle(self, rect):
    if rect == self._rect(self.startPoint, self.endPoint):
      return False

    v = rect.vertices()
    self.startPoint = v[3]
    self.endPoint = v[1]
    self.showRect(self.startPoint, self.endPoint)
    return True
Example #15
0
class DrawRect(QgsMapTool):
    '''Classe de sélection avec un Rectangle'''

    selectionDone = pyqtSignal()
    move = pyqtSignal()

    def __init__(self, iface, couleur):
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.iface = iface
        self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rb.setColor(couleur)
        self.reset()
        return None

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rb.reset(True)	 # true, its a polygon

    def canvasPressEvent(self, e):
        if not e.button() == Qt.LeftButton:
            return
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False
        if not e.button() == Qt.LeftButton:
            return None
        if self.rb.numberOfVertices() > 3:
            self.selectionDone.emit()
        else:
            width, height, ok = RectangleDialog().getSize()
            if width > 0 and height > 0 and ok:
                self.rb.addPoint(
                    QgsPointXY(
                        self.startPoint.x() + width,
                        self.startPoint.y() - height))
                self.showRect(
                    self.startPoint,
                    QgsPointXY(
                        self.startPoint.x() + width,
                        self.startPoint.y() - height))
                self.selectionDone.emit()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return
        self.move.emit()
        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rb.reset(QgsWkbTypes.PolygonGeometry)  # true, it's a polygon
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rb.addPoint(point1, False)
        self.rb.addPoint(point2, False)
        self.rb.addPoint(point3, False)
        self.rb.addPoint(point4, True)  # true to update canvas
        self.rb.show()

    def deactivate(self):
        self.rb.reset(True)
        QgsMapTool.deactivate(self)
Example #16
0
class DrawCircle(QgsMapTool):
    '''Outil de sélection par cercle, tiré de selectPlusFr'''

    selectionDone = pyqtSignal()
    move = pyqtSignal()

    def __init__(self, iface, color, segments):
        canvas = iface.mapCanvas()
        QgsMapTool.__init__(self, canvas)
        self.canvas = canvas
        self.iface = iface
        self.status = 0
        self.segments = segments
        self.rb = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rb.setColor(color)
        return None

    def canvasPressEvent(self, e):
        if not e.button() == Qt.LeftButton:
            return
        self.status = 1
        self.center = self.toMapCoordinates(e.pos())
        rbcircle(self.rb, self.center, self.center, self.segments)
        return

    def canvasMoveEvent(self, e):
        if not self.status == 1:
            return
        # construct a circle with N segments
        cp = self.toMapCoordinates(e.pos())
        rbcircle(self.rb, self.center, cp, self.segments)
        self.rb.show()
        self.move.emit()

    def canvasReleaseEvent(self, e):
        '''La sélection est faîte'''
        if not e.button() == Qt.LeftButton:
            return None
        self.status = 0
        if self.rb.numberOfVertices() > 3:
            self.selectionDone.emit()
        else:
            radius, ok = QInputDialog.getDouble(
                self.iface.mainWindow(), tr('Radius'),
                tr('Give a radius in m:'), min=0)
            if radius > 0 and ok:
                cp = self.toMapCoordinates(e.pos())
                cp.setX(cp.x() + radius)
                rbcircle(self.rb, self.toMapCoordinates(
                    e.pos()), cp, self.segments)
                self.rb.show()
                self.selectionDone.emit()
        return None

    def reset(self):
        self.status = 0
        self.rb.reset(True)

    def deactivate(self):
        self.rb.reset(True)
        QgsMapTool.deactivate(self)
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 EarthMineQGIS(QObject):
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        super(EarthMineQGIS, self).__init__()
        self.movingfeature = None
        self.iface = iface
        self.viewer = None
        self.canvas = self.iface.mapCanvas()
        self.settings = QSettings()
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value("locale/userLocale")[0:2]
        locale_path = os.path.join(self.plugin_dir, "i18n", "EarthMineQGIS_{}.qm".format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > "4.3.3":
                QCoreApplication.installTranslator(self.translator)

        self.pointtool = QgsMapToolEmitPoint(self.canvas)
        self.pointtool.canvasClicked.connect(self.set_viewer_location)

        self.settingsdialog = SettingsDialog(self.iface.mainWindow())

        self.actions = []
        self.menu = self.tr(u"&Earthmine")

        self.toolbar = self.iface.addToolBar(u"EarthMineQGIS")
        self.toolbar.setObjectName(u"EarthMineQGIS")

        self.legend = self.iface.legendInterface()

        emcolor = QColor(1, 150, 51)
        self.tempband = QgsRubberBand(self.canvas, QGis.Line)
        self.tempband.setWidth(5)
        self.tempband.setColor(emcolor)

        self.tempbandpoints = QgsRubberBand(self.canvas, QGis.Point)
        self.tempbandpoints.setWidth(7)
        self.tempbandpoints.setColor(emcolor)

        self.movingband = QgsRubberBand(self.canvas, QGis.Point)
        self.movingband.setWidth(5)
        self.movingband.setColor(emcolor)

        self.layersignals = []
        self.marker = None

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ":/icons/settings"
        self.add_action(
            icon_path, text=self.tr(u"Show Settings"), callback=self.show_settings, parent=self.iface.mainWindow()
        )
        icon_path = ":/icons/viewer"
        self.add_action(
            icon_path, text=self.tr(u"Earthmine Viewer"), callback=self.open_viewer, parent=self.iface.mainWindow()
        )

        self.marker = PostionMarker(self.canvas)
        self.marker.hide()

        self.viewer = Viewer(callbackobject=self)
        self.viewer.trackingChanged.connect(self.marker.setTracking)
        self.viewer.setLocationTriggered.connect(partial(self.canvas.setMapTool, self.pointtool))
        self.viewer.updateFeatures.connect(self.update_earthmine_features)
        self.viewer.layerChanged.connect(self.iface.setActiveLayer)
        self.viewer.clearLine.connect(self.clear_bands)
        self.viewer.closed.connect(self.remove_items)
        self.iface.currentLayerChanged.connect(self.viewer.update_current_layer)

        cursor = QCursor(QPixmap(":/icons/location"))
        self.pointtool.setCursor(cursor)
        self.pointtool.setAction(self.viewer.setlocationaction)

    def remove_items(self):
        self.marker.setTracking(False)
        self.disconnect_projectsignals()
        self.iface.actionPan().trigger()

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        self.canvas.scene().removeItem(self.marker)
        del self.marker

        self.disconnect_projectsignals()

        for action in self.actions:
            self.iface.removePluginMenu(self.tr(u"&Earthmine"), action)
            self.iface.removeToolBarIcon(action)

        del self.toolbar

        self.iface.removeDockWidget(self.viewer)
        self.viewer.deleteLater()

    def disconnect_projectsignals(self):
        safe_disconnect(QgsMapLayerRegistry.instance().layerWasAdded, self.connect_layer_signals)
        safe_disconnect(QgsMapLayerRegistry.instance().layersRemoved, self.layers_removed)
        safe_disconnect(self.canvas.layersChanged, self.layers_changed)
        safe_disconnect(self.iface.projectRead, self.connect_signals)
        safe_disconnect(self.canvas.selectionChanged, self.selection_changed)
        safe_disconnect(self.canvas.selectionChanged, self.viewer.selection_changed)

    def clear_bands(self):
        self.tempband.reset(QGis.Line)
        self.tempbandpoints.reset(QGis.Point)

    def visible_layers(self):
        """
        Return the visible layers shown in the map canvas
        :return:
        """
        return (layer for layer, visible in self.layers_with_states() if visible)

    def layers_with_states(self):
        for layer in maplayers():
            if not layer.type() == QgsMapLayer.VectorLayer:
                continue

            if not layer.geometryType() in [QGis.Point, QGis.Line]:
                continue

            yield layer, self.legend.isLayerVisible(layer)

    def _layer_feature_added(self, featureid):
        layer = self.sender()
        if not layer:
            return

        self.layer_feature_added(layer, featureid)

    def layer_feature_added(self, layer, featureid):
        if not self.viewer:
            return

        feature = layer.getFeatures(QgsFeatureRequest(featureid)).next()
        renderer = layer.rendererV2()
        transform = self.coordinatetransform(layer)
        featuredata = to_feature_data(layer.id(), feature, renderer, transform)
        geomtype = layer.geometryType()
        layerdata = dict(id=layer.id(), geomtype=QGis.vectorGeometryType(geomtype))
        self.viewer.load_features(layerdata, featuredata)

    def _layer_feature_delete(self, featureid):
        layer = self.sender()
        if not layer:
            return
        self.layer_feature_delete(layer, featureid)

    def layer_feature_delete(self, layer, featureid):
        if not self.viewer:
            return

        self.viewer.remove_feature(layer.id(), featureid)

    def _layer_geometry_changed(self, featureid, geometry):
        layer = self.sender()
        if not layer:
            return
        self.layer_geometry_changed(layer, featureid, geometry)

    def layer_geometry_changed(self, layer, featureid, geometry):
        if not self.viewer:
            return

        geomtype = layer.geometryType()
        if geomtype == QGis.Point:
            geom = geometry.asPoint()
            transform = self.coordinatetransform(layer)
            point = transform.transform(geom, QgsCoordinateTransform.ReverseTransform)
            location = dict(lat=point.y(), lng=point.x())
            self.viewer.edit_feature(layer.id(), featureid, [location])
        elif geomtype == QGis.Line:
            self.layer_feature_delete(layer, featureid)
            self.layer_feature_added(layer, featureid)

    def connect_layer_signals(self, layer):
        if not layer.type() == QgsMapLayer.VectorLayer:
            return

        layer.featureAdded.connect(self._layer_feature_added)
        layer.featureDeleted.connect(self._layer_feature_delete)
        layer.editingStarted.connect(self.layer_editstate_changed)
        layer.editingStopped.connect(self.layer_editstate_changed)
        # HACK The new style doesn't work here
        # http://hub.qgis.org/issues/6573
        signal = SIGNAL("geometryChanged(QgsFeatureId, QgsGeometry&)")
        self.connect(layer, signal, self._layer_geometry_changed)
        self.load_layer_features(layers=[layer])

    def layer_editstate_changed(self):
        layer = self.sender()
        if layer == self.iface.activeLayer():
            self.viewer.layer_changed(layer)

    def disconnect_signals(self):
        self.disconnect_projectsignals()

        for layer in maplayers():
            if not layer.type() == QgsMapLayer.VectorLayer:
                return

            safe_disconnect(layer.featureAdded, self._layer_feature_added)
            safe_disconnect(layer.featureDeleted, self._layer_feature_delete)
            safe_disconnect(layer.editingStarted, self.layer_editstate_changed)
            safe_disconnect(layer.editingStopped, self.layer_editstate_changed)
            # HACK The new style doesn't work here
            # http://hub.qgis.org/issues/6573
            signal = SIGNAL("geometryChanged(QgsFeatureId, QgsGeometry&)")
            self.disconnect(layer, signal, self._layer_geometry_changed)

    def connect_signals(self):
        for layer in maplayers():
            self.connect_layer_signals(layer)

        self.center_on_canvas()

    def set_viewer_location(self, point, mousebutton):
        transform = self.coordinatetransform()
        point = transform.transform(point, QgsCoordinateTransform.ReverseTransform)
        self.viewer.set_location(point)

    def distancearea(self):
        area = QgsDistanceArea()
        dest = self.canvas.mapRenderer().destinationCrs()
        area.setSourceCrs(dest)
        return area, dest.mapUnits()

    def coordinatetransform(self, layer=None):
        """
        Return the transform for WGS84 -> QGIS projection.
        """
        source = QgsCoordinateReferenceSystem()
        source.createFromWkt(
            'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]'
        )
        if not layer:
            dest = self.canvas.mapRenderer().destinationCrs()
        else:
            dest = layer.crs()
        transform = QgsCoordinateTransform(source, dest)
        return transform

    def earthmine_settings(self):
        settings = {}
        with settinggroup(self.settings, "plugins/Earthmine"):
            for key in ["serviceUrl", "baseDataUrl", "apiKey", "secretKey", "viewerUrl"]:
                if not self.settings.contains(key):
                    raise EarthmineSettingsError("{} not set".format(key))

                value = self.settings.value(key, type=str)
                if value is None:
                    raise EarthmineSettingsError("{} not set".format(key))

                settings[key] = value

        return settings

    @pyqtSlot()
    def ready(self):
        """
        Called when the viewer is ready to be started.  At this point the viewer hasn't been loaded
        so no other methods apart from startViewer will be handled.
        """
        settings = self.earthmine_settings()
        self.viewer.startViewer(settings)

    @pyqtSlot()
    def viewerReady(self):
        """
        Called once the viewer is loaded and ready to get location events.
        """
        self.disconnect_signals()
        self.connect_signals()
        self.iface.projectRead.connect(self.connect_signals)
        self.canvas.layersChanged.connect(self.layers_changed)
        self.canvas.selectionChanged.connect(self.selection_changed)
        self.canvas.selectionChanged.connect(self.viewer.selection_changed)
        QgsMapLayerRegistry.instance().layersRemoved.connect(self.layers_removed)
        QgsMapLayerRegistry.instance().layerWasAdded.connect(self.connect_layer_signals)
        self.center_on_canvas()
        self.viewer.activelayercombo.setLayer(self.iface.activeLayer())

    def center_on_canvas(self):
        point = self.canvas.extent().center()
        transform = self.coordinatetransform()
        point = transform.transform(point, QgsCoordinateTransform.ReverseTransform)
        self.viewer.set_location(point)
        self.viewer.infoaction.toggle()

    def selection_changed(self, layer):
        ids = [feature.id() for feature in layer.selectedFeatures()]
        if not ids:
            self.viewer.clear_selection(layer.id())
        else:
            self.viewer.set_selection(layer.id(), ids)

    def layers_changed(self):
        layerstates = self.layers_with_states()
        for layer, visible in layerstates:
            layerid = layer.id()
            viewerloaded = self.viewer.layer_loaded(layerid)
            QgsMessageLog.instance().logMessage(layerid, "Earthmine")
            QgsMessageLog.instance().logMessage("Viewer State:" + str(viewerloaded), "Earthmine")
            QgsMessageLog.instance().logMessage("QGIS State:" + str(visible), "Earthmine")
            if (viewerloaded and visible) or (not viewerloaded and not visible):
                QgsMessageLog.instance().logMessage("Ignoring as states match", "Earthmine")
                continue

            if viewerloaded and not visible:
                QgsMessageLog.instance().logMessage(
                    "Clearing layer because viewer loaded and disabled in QGIS", "Earthmine"
                )
                self.viewer.clear_layer_features(layerid)
                continue

            if not viewerloaded and visible:
                QgsMessageLog.instance().logMessage("Loading layer", "Earthmine")
                self.load_layer_features(layers=[layer])
                continue

    def layers_removed(self, layers):
        for layerid in layers:
            self.viewer.clear_layer_features(layerid)

    @pyqtSlot(str, float, float)
    def viewChanged(self, event, yaw, angle):
        self.marker.setAngle(angle)
        self.marker.setYaw(yaw)

    @pyqtSlot(str, str)
    def getInfo(self, layerid, featureid):
        featureid = int(featureid)
        activelayer = self.iface.activeLayer()
        if not activelayer:
            return

        activetool = self.viewer.active_tool()
        if not activetool in ["Info", "Select"]:
            return

        # Only show information for the active layer
        if not layerid == activelayer.id():
            return

        layer = layer_by_id(layerid)
        if activetool == "Select":
            layer.setSelectedFeatures([featureid])
        elif activetool == "Info":
            rq = QgsFeatureRequest(featureid)
            feature = layer.getFeatures(rq).next()
            dlg = get_feature_form(layer, feature)
            if dlg.dialog().exec_():
                self.canvas.refresh()

    @pyqtSlot(str, str, float, float, bool)
    def featureMoved(self, layerid, featureid, lat, lng, end):
        layer = layer_by_id(layerid)
        transform = self.coordinatetransform(layer)
        point = transform.transform(lng, lat)
        if not end:
            self.movingband.show()
            self.movingband.setToGeometry(QgsGeometry.fromPoint(point), layer)
            self.movingband.updatePosition()
            self.movingband.update()
        else:
            self.movingband.hide()
            feature = feature_by_id(layer, featureid)
            startpoint = feature.geometry().asPoint()
            dx = point.x() - startpoint.x()
            dy = point.y() - startpoint.y()
            layer.beginEditCommand("Feature Moved")
            # Block signals for this move as the geometry changed signal will re add the geometry on use.
            layer.blockSignals(True)
            layer.translateFeature(feature.id(), dx, dy)
            layer.blockSignals(False)
            self.canvas.refresh()
            layer.endEditCommand()

    @pyqtSlot(str, str)
    def onError(self, message, stacktrace=None):
        self.iface.messageBar().pushMessage("Earthmine", message, QgsMessageBar.WARNING)
        QgsMessageLog.logMessage(stacktrace, "Earthmine")

    @pyqtSlot(float, float, float)
    def addPoint(self, lat, lng, z):
        layer = self.viewer.active_layer
        if not layer.isEditable():
            self.iface.messageBar().pushMessage(
                "Earthmine",
                "Selected layer isn't editable. Please enable edit mode to add features",
                duration=3,
                level=QgsMessageBar.WARNING,
            )
            return

        transform = self.coordinatetransform(layer)
        point = transform.transform(lng, lat)
        geom = QgsGeometry.fromPoint(point)
        self.add_feature(layer, geom, z)

    def add_feature(self, layer, geom, z=None):
        feature = QgsFeature(layer.pendingFields())
        if z and self.viewer.copyZvalue:
            try:
                feature["Z"] = z
            except KeyError:
                QgsMessageLog.log("No Z found on layer {}".format(layer.name()))
                pass

        feature.setGeometry(geom)
        dlg = get_feature_form(layer, feature, isadd=True)
        if dlg.dialog().exec_():
            self.canvas.refresh()

    @pyqtSlot(str, bool, str)
    def drawLine(self, points, end, stats):
        points = json.loads(points)
        stats = json.loads(stats)
        QgsMessageLog.logMessage(str(stats), "Earthmine")
        self.tempband.reset(QGis.Line)
        self.tempbandpoints.reset(QGis.Point)
        color = QColor(self.viewer.current_action_color)
        self.tempband.setColor(color)
        self.tempbandpoints.setColor(color)

        layer = self.viewer.active_layer
        transform = self.coordinatetransform(layer)
        earthminepoints = []
        for point in points:
            newpoint = transform.transform(point["lng"], point["lat"])
            self.tempband.addPoint(newpoint)
            self.tempbandpoints.addPoint(newpoint)
            empoint = EarthminePoint(newpoint, point)
            earthminepoints.append(empoint)

        if end and not self.viewer.mode == "Vertical":
            geom = self.tempband.asGeometry()
            self.add_feature(layer, geom)
            self.clear_bands()

        self.viewer.geom = EarthmineLine(earthminepoints, stats)

        self.tempband.show()
        self.tempbandpoints.show()

    @pyqtSlot(str, str, str, float)
    def locationChanged(self, lat, lng, yaw, angle):
        transform = self.coordinatetransform()
        point = transform.transform(float(lng), float(lat))
        self.marker.setCenter(point)
        yaw = float(yaw)
        self.marker.setAngle(angle)
        self.marker.setYaw(yaw)
        self.marker.setTracking(self.viewer.tracking)

        if self.marker.tracking:
            rect = QgsRectangle(point, point)
            extentlimt = QgsRectangle(self.canvas.extent())
            extentlimt.scale(0.95)

            if not extentlimt.contains(point):
                self.canvas.setExtent(rect)
                self.canvas.refresh()

        # Clear old features
        self.viewer.clear_features()
        self.load_layer_features(point)

    def update_earthmine_features(self, viewfeatures):
        self.viewer.clear_features()

        if viewfeatures:
            self.load_layer_features()

    def load_layer_features(self, point=None, layers=None):

        # TODO Move this logic into the viewer and let it track it's position
        if point is None and self.marker.map_pos is None:
            return

        if point is None:
            point = self.marker.map_pos

        area, units = self.distancearea()
        rect = search_area(units, area, point)

        if layers is None:
            layers = self.visible_layers()

        for layer in layers:
            transform = self.coordinatetransform(layer)
            # Transform the rect
            source = self.canvas.mapRenderer().destinationCrs()
            dest = layer.crs()
            recttransform = QgsCoordinateTransform(source, dest)
            rect = recttransform.transformBoundingBox(rect)
            features = list(get_features_in_area(layer, rect, transform, self.canvas.mapSettings()))
            geomtype = layer.geometryType()
            layerdata = dict(id=layer.id(), geomtype=QGis.vectorGeometryType(geomtype))
            self.viewer.load_features(layerdata, features)

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate("EarthMineQGIS", message)

    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None,
    ):
        """Add a toolbar icon to the InaSAFE toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to s, "Earhtmine"how in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def open_viewer(self):
        """Run method that performs all the real work"""
        try:
            settings = self.earthmine_settings()
        except EarthmineSettingsError as ex:
            self.onError(ex.message)
            self.show_settings()
            return

        url = settings["viewerUrl"]
        if not url.startswith("http"):
            url = url.replace("\\\\", "\\")
            url = QUrl.fromLocalFile(url)
        else:
            url = QUrl(url)

        if not self.viewer.isVisible():
            self.iface.addDockWidget(Qt.RightDockWidgetArea, self.viewer)

        self.viewer.loadviewer(url)

    def show_settings(self):
        self.settingsdialog.show()
Example #19
0
class QgepMapToolConnectNetworkElements(QgsMapTool):
    """
    This map tool connects wastewater networkelements.

    It works on two lists of layers:
      source layers with fields with a foreign key to a networkelement
      target layers which depict networkelements (reaches and network nodes)

    The tool will snap to source layers first and once one is chosen to a target layer.

    It will then ask which field(s) should be connected and perform the update on the database
    """

    def __init__(self, iface, action):
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.iface = iface
        self.action = action

        self.rbline = QgsRubberBand(self.iface.mapCanvas(), QGis.Line)
        self.rbline.setColor(QColor('#f4530e'))
        self.rbline.setWidth(3)
        self.rbmarkers = QgsRubberBand(self.iface.mapCanvas(), QGis.Point)
        self.rbmarkers.setColor(QColor('#f4530e'))
        self.rbmarkers.setIconSize(6)

        self.source_snapper = QgepAreaSnapper(self.iface.mapCanvas())
        self.target_snapper = QgepAreaSnapper(self.iface.mapCanvas())

        self.source_feature = QgsFeature()
        self.rb_source_feature = QgsRubberBand(self.iface.mapCanvas())
        self.rb_source_feature.setColor(QColor('#f49e79'))
        self.rb_source_feature.setWidth(3)
        self.target_feature = QgsFeature()
        self.rb_target_feature = QgsRubberBand(self.iface.mapCanvas())
        self.rb_target_feature.setColor(QColor('#f49e79'))
        self.rb_target_feature.setWidth(3)

    def activate(self):
        """
        Called by QGIS whenever the tool is activated.
        """
        source_snap_layers = list()
        target_snap_layers = list()

        # A dict of layers and the fields that are foreign keys
        # pointing to wastewater networkelements
        self.network_element_sources = {
            QgepLayerManager.layer('vw_qgep_reach'): [
                ('rp_to_fk_wastewater_networkelement',
                 QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point To')),
                ('rp_from_fk_wastewater_networkelement',
                 QCoreApplication.translate('QgepMapToolConnectNetworkElements', 'Reach Point From'))
            ],
            QgepLayerManager.layer('od_catchment_area'): [
                ('fk_wastewater_networkelement_rw_current', QCoreApplication.translate(
                    'QgepMapToolConnectNetworkElements', 'Rainwater current')),
                ('fk_wastewater_networkelement_rw_planned', QCoreApplication.translate(
                    'QgepMapToolConnectNetworkElements', 'Rainwater planned')),
                ('fk_wastewater_networkelement_ww_current', QCoreApplication.translate(
                    'QgepMapToolConnectNetworkElements', 'Wastewater current')),
                ('fk_wastewater_networkelement_ww_planned', QCoreApplication.translate(
                    'QgepMapToolConnectNetworkElements', 'Wastewater planned'))
            ]
        }

        # A list of layers that can be used as wastewater networkelement
        # targets
        self.network_element_targets = [
            QgepLayerManager.layer('vw_wastewater_node'),
            QgepLayerManager.layer('vw_qgep_reach')
        ]

        for layer in self.network_element_sources.keys():
            if layer:
                snap_layer = QgsSnappingUtils.LayerConfig(
                    layer, QgsPointLocator.All, 16, QgsTolerance.Pixels)
                source_snap_layers.append(snap_layer)

        for layer in self.network_element_targets:
            if layer:
                snap_layer = QgsSnappingUtils.LayerConfig(
                    layer, QgsPointLocator.All, 16, QgsTolerance.Pixels)
                target_snap_layers.append(snap_layer)

        self.source_snapper.setLayers(source_snap_layers)
        self.source_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)

        self.target_snapper.setLayers(target_snap_layers)
        self.target_snapper.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)

        self.reset()

        self.action.setChecked(True)

        self.iface.mapCanvas().setCursor(QCursor(Qt.CrossCursor))

    def canvasMoveEvent(self, event):
        """
        When the mouse moves, update the rubberbands.
        """
        pt = event.originalMapPoint()
        snap_match = self.snapper.snapToMap(pt)

        if snap_match.isValid():
            if snap_match.type() != QgsPointLocator.Area:
                pt = snap_match.point()
            self.matchpoint = pt

            if self.source_match:
                if self.target_feature.id() != snap_match.featureId():
                    self.target_feature = self.get_feature_for_match(
                        snap_match)
                    self.rb_target_feature.setToGeometry(
                        self.target_feature.geometry(), snap_match.layer())
                self.rb_target_feature.show()
                self.rbmarkers.movePoint(pt)
            else:
                if self.source_feature.id() != snap_match.featureId():
                    self.source_feature = self.get_feature_for_match(
                        snap_match)
                    self.rb_source_feature.setToGeometry(
                        self.source_feature.geometry(), snap_match.layer())
                self.rb_source_feature.show()
                self.rbmarkers.movePoint(pt, 0)
            self.rbmarkers.show()
        else:
            self.rbmarkers.hide()
            if self.source_match:
                self.rb_target_feature.hide()
            else:
                self.rb_source_feature.hide()

        self.rbline.movePoint(pt)

        self.snapresult = snap_match

    def canvasReleaseEvent(self, event):
        """
        On a click update the rubberbands and the snapping results if it's a left click. Reset if it's a right click.
        """
        if event.button() == Qt.LeftButton:
            if self.snapresult.isValid():
                if self.source_match:
                    self.connect_features(self.source_match, self.snapresult)
                else:
                    self.rbline.show()
                    self.rbline.addPoint(self.matchpoint)
                    self.source_match = self.snapresult
                    self.snapper = self.target_snapper
        else:
            self.reset()

    def deactivate(self):
        """
        Called by QGIS whenever this tool is deactivated.
        """
        self.reset()
        self.action.setChecked(False)

    def reset(self):
        """
        Resets the tool to a pristine state
        """
        self.source_match = None
        self.rbline.hide()
        self.rbline.reset()
        self.rbmarkers.hide()
        self.rbmarkers.reset(QGis.Point)
        self.rbmarkers.addPoint(QgsPoint())
        self.snapresult = None
        self.source_match = None
        self.snapper = self.source_snapper
        self.source_feature = QgsFeature()
        self.target_feature = QgsFeature()
        self.rb_source_feature.reset()
        self.rb_target_feature.reset()

    def get_feature_for_match(self, match):
        """
        Get the feature for a snapping result
        @param match: The QgsPointLocator.SnapMatch object
        @return: A feature
        """
        return next(match.layer().getFeatures(QgsFeatureRequest().setFilterFid(match.featureId())))

    def connect_features(self, source, target):
        """
        Connects the source feature with the target feature.

        @param source: A QgsPointLocator.Match object. Its foreign key will be updated.
                       A dialog will be opened which asks the user for which foreign key(s) he wants to update.
        @param target: A QgsPointLocator.Match object. This feature will be used as link target.
                       Its obj_id attribute will be used as primary key.
        """
        dlg = QDialog(self.iface.mainWindow())
        dlg.setWindowTitle(self.tr('Select properties to connect'))
        dlg.setLayout(QFormLayout())

        properties = list()

        for prop in self.network_element_sources[source.layer()]:
            cbx = QCheckBox(prop[1])
            cbx.setObjectName(prop[0])
            properties.append(cbx)
            dlg.layout().addWidget(cbx)

        btn_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        dlg.layout().addWidget(btn_box)
        btn_box.accepted.connect(dlg.accept)
        btn_box.rejected.connect(dlg.reject)

        source_feature = self.get_feature_for_match(source)
        target_feature = self.get_feature_for_match(target)

        if dlg.exec_():
            for cbx in properties:
                if cbx.isChecked():
                    source_feature[cbx.objectName()] = target_feature['obj_id']
            if source.layer().updateFeature(source_feature):
                self.iface.messageBar().pushMessage('QGEP',
                                                    self.tr('Connected {} to {}').format(
                                                        source_feature[
                                                            'identifier'],
                                                        target_feature['identifier']),
                                                    QgsMessageBar.INFO, 5)
            else:
                self.iface.messageBar().pushMessage('QGEP',
                                                    self.tr(
                                                        'Error connecting features'),
                                                    QgsMessageBar.WARNING, 5)

        self.reset()
class RectangleAreaTool(QgsMapTool):

    rectangleCreated = pyqtSignal(float, float, float, float)

    def __init__(self, canvas, action):
        QgsMapTool.__init__(self, canvas)

        self.canvas = canvas
        self.active = False
        self.setAction(action)
        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        mFillColor = QColor(254, 178, 76, 63)
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

    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
        self.rubberBand.hide()
        self.transformCoordinates()
        self.rectangleCreated.emit(self.startPoint.x(), self.startPoint.y(), self.endPoint.x(), self.endPoint.y())

    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(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return
        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        self.rubberBand.addPoint(point4, True)    # true to update canvas
        self.rubberBand.show()

    def transformCoordinates(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

        # Defining the crs from src and destiny
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        crsSrc = QgsCoordinateReferenceSystem(epsg)
        crsDest = QgsCoordinateReferenceSystem(4326)
        # Creating a transformer
        coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance())
        # Transforming the points
        self.startPoint = coordinateTransformer.transform(self.startPoint)
        self.endPoint = coordinateTransformer.transform(self.endPoint)

    def deactivate(self):
        self.rubberBand.hide()
        QgsMapTool.deactivate(self)
Example #21
0
class Dialog(QDialog, Ui_nbEditor_dialog):         
    def __init__(self, iface, ml, mc):
        """Constructor for the dialog.
        
        Args:
          iface: QgsInterface instance.
        """
        
        QDialog.__init__(self, iface.mainWindow())                               
                        
        self.setupUi(self)
        
        self.ml = ml
        self.mCanvas = mc         
        self.mRubberBand = QgsRubberBand(self.mCanvas, True)
        self.mRubberBand.reset(QGis.Polygon)
        self.mRubberBand.setColor(Qt.red)
        self.mRubberBand.setWidth(2)
        self.ids = []
        
        self.ini(0)
        
        self.pushCancel.clicked.connect(self.close)
        self.pushOK.clicked.connect(self.convert)
        self.comboBox.addItems(['','Intersections','Touches','Within distance']) 
        
        self.comboBox.currentIndexChanged.connect(self.nbMethod)         
        self.ml.selectionChanged.connect(self.map2tab)
        
        
    def ini(self, n):
        self.model = QStandardItemModel(n, 1)
        self.tableView.setModel(self.model)
        self.model.setHeaderData(0, Qt.Horizontal, 'Neighbouring IDs')
        self.tableView.setSelectionMode(QAbstractItemView.SingleSelection)        
        self.selectionModel = QItemSelectionModel(self.model)
        self.tableView.setSelectionModel(self.selectionModel)
        self.tableView.horizontalHeader().setStretchLastSection(True)    
        self.tableView.selectionModel().selectionChanged.connect(self.tab2map)
        self.progressBar.setValue(0)


    def settings(self):
        self.mod = min(self.ids)
        self.p = 1
        if self.mod==1:
            self.p = 0


    def map2tab(self):
        s = ''
        idx = self.tableView.selectionModel().selectedIndexes()[0]
        ts = str(self.model.itemData(idx)[0])
        
        for fid in sorted(self.ml.selectedFeaturesIds()):
            s += '%s,' % str(int(fid)+self.p)                   
 
        s = s[:-1]
         
        if s!=ts:
            self.model.setData(idx, s)
         
        # in order to handle the symmetry
        if len(s)>len(ts):
             iLst = s.strip().replace(' ', '').split(',')
             jLst = ts.strip().replace(' ', '').split(',')
        else:
             iLst = ts.strip().replace(' ', '').split(',')
             jLst = s.strip().replace(' ', '').split(',')
              
        cent = str(idx.row()+self.p)
        dLst = list(set(iLst)-set(jLst))
          
        for d in dLst:              
            row = int(d)-self.p
            sor = str(self.model.itemData(self.model.index(row, 0))[0])
            eLst = sor.strip().replace(' ', '').split(',')            
            res = ''
            if cent in set(eLst):
                ii = eLst.index(cent)                
                del eLst[ii]
                eLst = sorted(map(int, eLst))                
                for e in eLst:
                    res += '%s,' % e
                res = res[:-1]
            else:
                u = sor + ',%s' % cent
                eLst = sorted(map(int, u.strip().replace(' ', '').split(',')))
                for e in eLst:
                    res += '%s,' % e
                res = res[:-1]                 
                               
            self.model.setData(self.model.index(row, 0, QModelIndex()), res)
                   
                
    def nbWithinDist(self):
        dlg = xdist.Dialog()
        dlg.setModal(True)
        dlg.setWindowTitle("Between two objects")
                
        if dlg.exec_() == QDialog.Accepted:
            lDist = float(dlg.lineEdit.text())
            if lDist==0:
                return

            feat = QgsFeature()
            provider = self.ml.dataProvider()
            e = provider.featureCount()

            self.settings()

            for ne in range(self.mod, e + self.mod):
                feat = QgsFeature()
                geom = QgsGeometry()
                fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
                if fiter.nextFeature(feat):
                    geom = QgsGeometry(feat.geometry())

                neighbours = self.hdist(feat, lDist)
                row = feat.id()-self.mod
                self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours)
                self.progressBar.setValue(100*ne/e)


    def hdist(self, feata, lDist):
        geoma = QgsGeometry(feata.geometry()) 
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) 
        ne = 0              
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)                       
            geomb = QgsGeometry(feat.geometry())            
            if feata.id()!=feat.id():
                if geoma.distance(geomb)<=lDist:
                    neighbours = neighbours + '%s,' % (feat.id()+self.p)
        return neighbours[:-1]            
    
    
    def tab2map(self):        
        QApplication.setOverrideCursor(Qt.WaitCursor)
        
        self.ml.selectionChanged.disconnect(self.map2tab)
        
        idx = self.tableView.selectionModel().selectedIndexes()[0]
        featureId = idx.row() + self.p
        
        s = self.model.itemData(idx)
        lst = s[0].strip().replace(' ', '').split(',')
        
        self.ml.removeSelection()
        
        for sid in lst:
            self.ml.select(int(sid)-self.p)
              
        provider = self.ml.dataProvider()        
        
        feat = QgsFeature()
        layer = QgsVectorLayerCache(self.ml, provider.featureCount())
        layer.featureAtId(idx.row()+self.mod, feat)
        geom = QgsGeometry(feat.geometry())   
        
        self.mRubberBand.setToGeometry(geom, self.ml)
        self.mRubberBand.show()
        
        self.ml.selectionChanged.connect(self.map2tab)
        
        QApplication.restoreOverrideCursor()        
        
        
    def closeEvent(self,event):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.ml.selectionChanged.disconnect(self.map2tab)
        self.ml.removeSelection()
        self.mRubberBand.hide()
        self.close()

        QApplication.restoreOverrideCursor()

        
    def convert(self):
        dlg = editor.Dialog()
        dlg.setModal(True)
        dlg.setWindowTitle("Neighbour list in BUGS format")
        num = ""
        adj = ""
        sumNumNeigh = 0
        for row in range(0, self.model.rowCount()):
            ts = self.model.itemData(self.model.index(row, 0))
            lst = ts[0].strip().replace(' ', '').split(',')
            num += '%s, ' % len(lst)
            sumNumNeigh += len(lst)
            lst.reverse()
            sor = ', '.join(lst) + ','
            adj = adj + str(sor) + '\n' 
        
        num = num[:-2]
        adj = adj[:-2]
        
        nblist = 'list(\nnum = c(%s),\nadj = c(%s),\nsumNumNeigh=%s)' % (num, adj, sumNumNeigh)
        dlg.plainTextEdit.appendPlainText(nblist)
        
        dlg.exec_()                


    def nbMethod(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.ml.selectionChanged.disconnect(self.map2tab)
        self.model.removeRows(0, self.model.rowCount(QModelIndex()), QModelIndex())
        n = self.ml.dataProvider().featureCount()
        self.ini(n)

        self.ids = []

        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, n))
        ne = 0
        feat = QgsFeature()
        while feats.nextFeature(feat):
            ne += 1
            self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)
            self.ids.append(feat.id())

        if self.comboBox.currentText()=="Touches":            
            if self.ml.geometryType()==0:
                return
            else:
                self.nbTouches()
        if self.comboBox.currentText()=="Intersections":
            if self.ml.geometryType()==0:
                return
            else:
                self.nbIntersects()
        if self.comboBox.currentText()=="Within distance":
            self.nbWithinDist()

        self.ml.selectionChanged.connect(self.map2tab)

        QApplication.restoreOverrideCursor()
        
            
    def nbTouches(self):                                
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        e = provider.featureCount()

        self.settings()

        for ne in range(self.mod, e + self.mod):
            feat = QgsFeature()
            geom = QgsGeometry()
            fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
            if fiter.nextFeature(feat):
                geom = QgsGeometry(feat.geometry())

            neighbours = self.htouch(feat)
            row = feat.id()-self.mod    
            self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours)
            self.progressBar.setValue(100*ne/e)

         
    def htouch(self, feata):
        geoma = QgsGeometry(feata.geometry()) 
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) 
        ne = 0              
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)                       
            geomb = QgsGeometry(feat.geometry())
            if feata.id()!=feat.id():
                if geoma.touches(geomb)==True:
                    neighbours = neighbours + '%s,' % (feat.id()+self.p)                
        return neighbours[:-1]

    
    def nbIntersects(self):
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        e = provider.featureCount()

        self.settings()

        for ne in range(self.mod, e + self.mod):
            feat = QgsFeature()
            geom = QgsGeometry()
            fiter = self.ml.getFeatures(QgsFeatureRequest(ne))
            if fiter.nextFeature(feat):
                geom = QgsGeometry(feat.geometry())

            neighbours = self.hintersect(feat)
            row = feat.id()-self.mod
            self.model.setData(self.model.index(row, 0, QModelIndex()), neighbours)
            self.progressBar.setValue(100*ne/e)


    def hintersect(self, feata):
        geoma = QgsGeometry(feata.geometry())  
        feat = QgsFeature()
        provider = self.ml.dataProvider()
        feats = provider.getFeatures()
        self.emit(SIGNAL("runStatus(PyQt_PyObject)"), 0)
        self.emit(SIGNAL("runRange(PyQt_PyObject)"), (0, provider.featureCount())) 
        ne = 0              
        neighbours = ""
        while feats.nextFeature(feat):
            ne += 1
            self.emit(SIGNAL("runStatus(PyQt_PyObject)"), ne)                       
            geomb = QgsGeometry(feat.geometry())
            if feata.id()!=feat.id():
                if geoma.intersects(geomb)==True:
                    neighbours = neighbours + '%s,' % (feat.id()+self.p)
        return neighbours[:-1]
Example #22
0
class ShapeTool(QgsMapTool):
    #signal emitted when the mouse is clicked. This indicates that the tool finished its job
    toolFinished = pyqtSignal()
    def __init__(self, canvas, geometryType, param, type, color = QColor( 254, 178, 76, 63 )):
        """
        Constructor
        """
        QgsMapTool.__init__(self, canvas)
        self.canvas = canvas
        self.active = False
        self.geometryType = geometryType
        self.param=param
        self.type=type       
        self.cursor=None
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)     
        self.setColor(color)
        self.reset()
        self.rotAngle = 0
        self.currentCentroid = None
        self.rotate = False
    
    def setColor(self, mFillColor):
        """
        Adjusting the color to create the rubber band
        """
    
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(1)
    
    def reset(self):
        """
        Resetting the rubber band
        """
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        try:
            self.rubberBand.reset(QGis.Polygon)
        except:
            pass

    def rotateRect(self, centroid, e):
        """
        Calculates the angle for the rotation.
        """
        item_position = self.canvas.mapToGlobal(e.pos())
        c = self.toCanvasCoordinates(centroid)
        c = self.canvas.mapToGlobal(c)
        rotAngle = pi - atan2(item_position.y() - c.y(), item_position.x() - c.x())
        return rotAngle

    def canvasPressEvent(self, e):
        """
        When the canvas is pressed the tool finishes its job
        """
        # enforce mouse restoring if clicked right after rotation 
        QtGui.QApplication.restoreOverrideCursor()
        self.canvas.unsetMapTool(self)
        self.toolFinished.emit()

    def canvasMoveEvent(self, e):
        """
        Deals with mouse move event to update the rubber band position in the canvas
        """
        ctrlIsHeld = QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ControlModifier
        if e.button() != None and not ctrlIsHeld:
            if self.rotate:
                # change rotate status
                self.rotate = False
            QtGui.QApplication.restoreOverrideCursor()
            self.endPoint = self.toMapCoordinates( e.pos() )
        elif e.button() != None and ctrlIsHeld \
            and self.geometryType == self.tr(u"Square"):
            # calculate angle between mouse and last rubberband centroid before holding control
            self.rotAngle = self.rotateRect(self.currentCentroid, e)
            if not self.rotate:
                # only override mouse if it is not overriden already
                QtGui.QApplication.setOverrideCursor(QCursor(Qt2.BlankCursor))
                self.rotate = True
        if self.geometryType == self.tr(u"Circle"):
                self.showCircle(self.endPoint)
        elif self.geometryType == self.tr(u"Square"):
            self.showRect(self.endPoint, sqrt(self.param)/2, self.rotAngle)
    
    def showCircle(self, startPoint):
        """
        Draws a circle in the canvas
        """
        nPoints = 50
        x = startPoint.x()
        y = startPoint.y()
        if self.type == self.tr('distance'):
            r = self.param
            self.rubberBand.reset(QGis.Polygon)
            for itheta in range(nPoints+1):
                theta = itheta*(2.0*pi/nPoints)
                self.rubberBand.addPoint(QgsPoint(x+r*cos(theta), y+r*sin(theta)))
            self.rubberBand.show()
        else:
            r = sqrt(self.param/pi)
            self.rubberBand.reset(QGis.Polygon)
            for itheta in range(nPoints+1):
                theta = itheta*(2.0*pi/nPoints)
                self.rubberBand.addPoint(QgsPoint(x+r*cos(theta), y+r*sin(theta)))
            self.rubberBand.show()

    def showRect(self, startPoint, param, rotAngle=0):
        """
        Draws a rectangle in the canvas
        """  
        self.rubberBand.reset(QGis.Polygon)
        x = startPoint.x() # center point x
        y = startPoint.y() # center point y
        # rotation angle is always applied in reference to center point
        # to avoid unnecessary calculations
        c = cos(rotAngle)
        s = sin(rotAngle)
        # translating coordinate system to rubberband centroid
        point1 = QgsPoint((- param), (- param))
        point2 = QgsPoint((- param), ( param))
        point3 = QgsPoint((param), ( param))
        point4 = QgsPoint((param), (- param))
        # rotating and moving to original coord. sys.
        point1_ = QgsPoint(point1.x()*c - point1.y()*s + x, point1.y()*c + point1.x()*s + y)
        point2_ = QgsPoint(point2.x()*c - point2.y()*s + x, point2.y()*c + point2.x()*s + y)
        point3_ = QgsPoint(point3.x()*c - point3.y()*s + x, point3.y()*c + point3.x()*s + y)
        point4_ = QgsPoint(point4.x()*c - point4.y()*s + x, point4.y()*c + point4.x()*s + y)
        self.rubberBand.addPoint(point1_, False)
        self.rubberBand.addPoint(point2_, False)
        self.rubberBand.addPoint(point3_, False)
        self.rubberBand.addPoint(point4_, True)
        self.rubberBand.show()
        self.currentCentroid = startPoint
        
    def deactivate(self):
        """
        Deactivates the tool and hides the rubber band
        """
        self.rubberBand.hide()
        QgsMapTool.deactivate(self)
        # restore mouse in case tool is disabled right after rotation
        QtGui.QApplication.restoreOverrideCursor()
        
    def activate(self):
        """
        Activates the tool
        """
        QgsMapTool.activate(self)
    
    def reproject(self, geom, canvasCrs):
        """
        Reprojects geom from the canvas crs to the reference crs
        geom: geometry to be reprojected
        canvasCrs: canvas crs (from crs)
        """
        destCrs = self.reference.crs()
        if canvasCrs.authid() != destCrs.authid():
            coordinateTransformer = QgsCoordinateTransform(canvasCrs, destCrs)
            geom.transform(coordinateTransformer)
class RotateRasterMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rasterShadow = RasterShadowMapCanvasItem(self.canvas)

        self.rubberBandExtent = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.setColor(Qt.red)
        self.rubberBandExtent.setWidth(1)

        # In case of rotation around pressed point (ctrl)
        # Use rubberBand for displaying an horizontal line.
        self.rubberBandDisplacement = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandDisplacement.setColor(Qt.red)
        self.rubberBandDisplacement.setWidth(1)

        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()
        self.layer = None

    def canvasPressEvent(self, e):
        self.startY = e.pos().y()
        self.endY = self.startY
        self.isEmittingPoint = True
        self.height = self.canvas.height()

        modifiers = QApplication.keyboardModifiers()
        self.isRotationAroundPoint = bool(modifiers & Qt.ControlModifier)
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint

        self.isLayerVisible = isLayerVisible(self.iface, self.layer)
        setLayerVisible(self.iface, self.layer, False)

        rotation = self.computeRotation()
        self.showRotation(rotation)

        self.layer.history.append({"action": "rotation", "rotation": self.layer.rotation, 
            "center": self.layer.center}) # rotation set
        
    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()

        rotation = self.computeRotation()
        if self.isRotationAroundPoint:
            self.layer.moveCenterFromPointRotate(
                self.startPoint, rotation, 1, 1)
        val = self.layer.rotation + rotation
    
        self.layer.setRotation(val)

        setLayerVisible(self.iface, self.layer,
                        self.isLayerVisible)
        self.layer.repaint()

        self.layer.commitTransformParameters()


    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endY = e.pos().y()
        rotation = self.computeRotation()
        self.showRotation(rotation)

        self.endPoint = self.toMapCoordinates(e.pos())

    def computeRotation(self):
        if self.isRotationAroundPoint:
            dX = self.endPoint.x() - self.startPoint.x()
            dY = self.endPoint.y() - self.startPoint.y()
            return math.degrees(math.atan2(-dY, dX))
        else:
            dY = self.endY - self.startY
            return 90.0 * dY / self.height

    def showRotation(self, rotation):
        if self.isRotationAroundPoint:
            cornerPoints = self.layer.transformedCornerCoordinatesFromPoint(
                self.startPoint, rotation, 1, 1)

            self.rasterShadow.reset(self.layer)
            self.rasterShadow.setDeltaRotationFromPoint(
                rotation, self.startPoint, True)
            self.rasterShadow.show()

            self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
            point0 = QgsPointXY(self.startPoint.x() + 10, self.startPoint.y())
            point1 = QgsPointXY(self.startPoint.x(), self.startPoint.y())
            point2 = QgsPointXY(self.endPoint.x(), self.endPoint.y())
            self.rubberBandDisplacement.addPoint(point0, False)
            self.rubberBandDisplacement.addPoint(point1, False)
            self.rubberBandDisplacement.addPoint(
                point2, True)  # true to update canvas
            self.rubberBandDisplacement.show()
        else:
            center, originalRotation, xScale, yScale = \
                self.layer.transformParameters()
            newRotation = rotation + originalRotation
            cornerPoints = self.layer.transformedCornerCoordinates(
                center, newRotation, xScale, yScale)

            self.rasterShadow.reset(self.layer)
            self.rasterShadow.setDeltaRotation(rotation, True)
            self.rasterShadow.show()

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in cornerPoints:
            self.rubberBandExtent.addPoint(point, False)
        # for closing
        self.rubberBandExtent.addPoint(cornerPoints[0], True)
        self.rubberBandExtent.show()
Example #24
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()"))
class ScaleRasterMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rasterShadow = RasterShadowMapCanvasItem(self.canvas)

        self.rubberBandExtent = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.setColor(Qt.red)
        self.rubberBandExtent.setWidth(1)

        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()
        self.layer = None

    def canvasPressEvent(self, e):
        pressed_button = e.button()
        if pressed_button == 1:
            self.startPoint = e.pos()
            self.endPoint = self.startPoint
            self.isEmittingPoint = True
            self.height = float(self.canvas.height())
            self.width = float(self.canvas.width())

            modifiers = QApplication.keyboardModifiers()
            self.isKeepRelativeScale = bool(modifiers & Qt.ControlModifier)

            self.isLayerVisible = isLayerVisible(self.iface,
                                                 self.layer)
            setLayerVisible(self.iface, self.layer, False)

            scaling = self.computeScaling()
            self.showScaling(*scaling)
        self.layer.history.append({"action": "scale", "xScale": self.layer.xScale, "yScale": self.layer.yScale})

    def canvasReleaseEvent(self, e):
        pressed_button = e.button()
        if pressed_button == 1:
            self.isEmittingPoint = False

            self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
            self.rasterShadow.reset()

            xScale, yScale = self.computeScaling()
            self.layer.setScale(xScale * self.layer.xScale,
                                yScale * self.layer.yScale)

            setLayerVisible(self.iface, self.layer, self.isLayerVisible)
        elif pressed_button == 2:
            number, ok = QInputDialog.getText(
                None, "Scale & DPI", "Enter scale,dpi (e.g. 3000,96)")
            if not ok:
                self.layer.history.pop()
                return
            scales = number.split(',')
            if len(scales) != 2:
                self.layer.history.pop()
                QMessageBox.information(
                    self.iface.mainWindow(),
                    "Error",
                    "Must be 2 numbers")
                return
            scale = tryfloat(scales[0])
            dpi = tryfloat(scales[1])
            if scale and dpi:
                xScale = scale / (dpi / 0.0254)
                yScale = xScale
            else:
                self.layer.history.pop()
                QMessageBox.information(
                    self.iface.mainWindow(),
                    "Error",
                    "Bad format: Must be scale,dpi (e.g. 3000,96)")
                return

            self.layer.setScale(xScale, yScale)

        self.layer.repaint()
        self.layer.commitTransformParameters()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = e.pos()
        scaling = self.computeScaling()
        self.showScaling(*scaling)

    def computeScaling(self):
        dX = -(self.endPoint.x() - self.startPoint.x())
        dY = self.endPoint.y() - self.startPoint.y()
        xScale = 1.0 - (dX / (self.width * 1.1))
        yScale = 1.0 - (dY / (self.height * 1.1))

        if self.isKeepRelativeScale:
            # keep same scale in both dimensions
            return (xScale, xScale)
        else:
            return (xScale, yScale)

    def showScaling(self, xScale, yScale):
        if xScale == 0 and yScale == 0:
            return

        center, rotation, originalXScale, originalYScale = \
            self.layer.transformParameters()
        newXScale = xScale * originalXScale
        newYScale = yScale * originalYScale
        cornerPoints = self.layer.transformedCornerCoordinates(
            center, rotation, newXScale, newYScale)

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in cornerPoints:
            self.rubberBandExtent.addPoint(point, False)
        # for closing
        self.rubberBandExtent.addPoint(cornerPoints[0], True)
        self.rubberBandExtent.show()

        self.rasterShadow.reset(self.layer)
        self.rasterShadow.setDeltaScale(xScale, yScale, True)
        self.rasterShadow.show()
Example #26
0
class RectangleMapTool(QgsMapToolEmitPoint):
  def __init__(self, canvas):
    self.canvas = canvas
    QgsMapToolEmitPoint.__init__(self, self.canvas)

    self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
    self.rubberBand.setColor(QColor(255, 0, 0, 180))
    self.rubberBand.setWidth(1)
    self.reset()

  def reset(self):
    self.startPoint = self.endPoint = None
    self.isEmittingPoint = False
    self.rubberBand.reset(QGis.Polygon)

  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
    self.emit(SIGNAL("rectangleCreated()"))

  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(QGis.Polygon)
    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, True)	# true to update canvas
    self.rubberBand.show()

  def rectangle(self):
    if self.startPoint == None or self.endPoint == 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 setRectangle(self, rect):
    if rect == self.rectangle():
      return False

    if rect == None:
      self.reset()
    else:
      self.startPoint = QgsPoint(rect.xMaximum(), rect.yMaximum())
      self.endPoint = QgsPoint(rect.xMinimum(), rect.yMinimum())
      self.showRect(self.startPoint, self.endPoint)
    return True
class MoveRasterMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rasterShadow = RasterShadowMapCanvasItem(self.canvas)

        self.rubberBandDisplacement = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandDisplacement.setColor(Qt.red)
        self.rubberBandDisplacement.setWidth(1)

        self.rubberBandExtent = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.setColor(Qt.red)
        self.rubberBandExtent.setWidth(1)

        self.isLayerVisible = True

        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()
        self.layer = None

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint
        self.isEmittingPoint = True
        self.originalCenter = self.layer.center
        # this tool do the displacement itself TODO update so it is done by
        # transformed coordinates + new center)
        self.originalCornerPoints = self.layer.transformedCornerCoordinates(
            *self.layer.transformParameters())

        self.isLayerVisible = isLayerVisible(self.iface, self.layer)
        setLayerVisible(self.iface, self.layer, False)

        self.showDisplacement(self.startPoint, self.endPoint)
        self.layer.history.append({"action": "move", "center": self.layer.center})

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False

        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()

        x = self.originalCenter.x() + self.endPoint.x() - self.startPoint.x()
        y = self.originalCenter.y() + self.endPoint.y() - self.startPoint.y()
        self.layer.setCenter(QgsPointXY(x, y))

        setLayerVisible(self.iface, self.layer,
                        self.isLayerVisible)
        self.layer.repaint()

        self.layer.commitTransformParameters()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())
        self.showDisplacement(self.startPoint, self.endPoint)

    def showDisplacement(self, startPoint, endPoint):
        self.rubberBandDisplacement.reset(QgsWkbTypes.LineGeometry)
        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(endPoint.x(), endPoint.y())
        self.rubberBandDisplacement.addPoint(point1, False)
        self.rubberBandDisplacement.addPoint(
            point2, True)  # true to update canvas
        self.rubberBandDisplacement.show()

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in self.originalCornerPoints:
            self._addDisplacementToPoint(self.rubberBandExtent, point, False)
        # for closing
        self._addDisplacementToPoint(
            self.rubberBandExtent, self.originalCornerPoints[0], True)
        self.rubberBandExtent.show()

        self.rasterShadow.reset(self.layer)
        self.rasterShadow.setDeltaDisplacement(self.endPoint.x(
        ) - self.startPoint.x(), self.endPoint.y() - self.startPoint.y(), True)
        self.rasterShadow.show()

    def _addDisplacementToPoint(self, rubberBand, point, doUpdate):
        x = point.x() + self.endPoint.x() - self.startPoint.x()
        y = point.y() + self.endPoint.y() - self.startPoint.y()
        self.rubberBandExtent.addPoint(QgsPointXY(x, y), doUpdate)
Example #28
0
class MapWidget(Ui_CanvasWidget, QMainWindow):
    def __init__(self, parent=None):
        super(MapWidget, self).__init__(parent)
        self.setupUi(self)

        self.firstshow = True
        self.layerbuttons = []
        self.editfeaturestack = []
        self.lastgpsposition = None
        self.project = None
        self.gps = None
        self.gpslogging = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)

        if hasattr(self.canvas, 'setParallelRenderingEnabled'):
            self.canvas.setParallelRenderingEnabled(True)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)

        self.editgroup = QActionGroup(self)
        self.editgroup.setExclusive(True)
        self.editgroup.addAction(self.actionPan)
        self.editgroup.addAction(self.actionZoom_In)
        self.editgroup.addAction(self.actionZoom_Out)
        self.editgroup.addAction(self.actionInfo)

        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self)
        self.projecttoolbar.addAction(self.actionGPS)

        gpsspacewidget= QWidget()
        gpsspacewidget.setMinimumWidth(30)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        self.dataentryselection = QAction(self.projecttoolbar)
        self.dataentryaction = self.projecttoolbar.insertAction(self.topspaceraction, self.dataentryselection)
        self.dataentryselection.triggered.connect(self.select_data_entry)

        self.marker = GPSMarker(self.canvas)
        self.marker.hide()

        self.currentfeatureband = QgsRubberBand(self.canvas)
        self.currentfeatureband.setIconSize(20)
        self.currentfeatureband.setWidth(10)
        self.currentfeatureband.setColor(QColor(186, 93, 212, 76))

        self.gpsband = QgsRubberBand(self.canvas)
        self.gpsband.setColor(QColor(0, 0, 212, 76))
        self.gpsband.setWidth(5)

        RoamEvents.editgeometry.connect(self.queue_feature_for_edit)
        RoamEvents.selectioncleared.connect(self.clear_selection)
        RoamEvents.selectionchanged.connect(self.highlight_selection)
        RoamEvents.featureformloaded.connect(self.feature_form_loaded)

        self.connectButtons()

    def init_qgisproject(self, doc):
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        #red = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorRedPart", 255 )[0];
        #green = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorGreenPart", 255 )[0];
        #blue = QgsProject.instance().readNumEntry( "Gui", "/CanvasColorBluePart", 255 )[0];
        #color = QColor(red, green, blue);
        #self.canvas.setCanvasColor(color)
        self.canvas.updateScale()
        return self.canvas.mapRenderer().destinationCrs()

    def showEvent(self, *args, **kwargs):
        if self.firstshow:
            self.canvas.refresh()
            self.canvas.repaint()
            self.firstshow = False

    def feature_form_loaded(self, form, feature, project, editmode):
        self.currentfeatureband.setToGeometry(feature.geometry(), form.QGISLayer)

    def highlight_selection(self, results):
        self.clear_selection()
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 200))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def highlight_active_selection(self, layer, feature, features):
        self.clear_selection()
        self.highlight_selection({layer: features})
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)

    def clear_selection(self):
        # Clear the main selection rubber band
        self.currentfeatureband.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

        self.editfeaturestack = []

    def queue_feature_for_edit(self, form, feature):
        def trigger_default_action():
            for action in self.projecttoolbar.actions():
                if action.property('dataentry') and action.isdefault:
                    action.trigger()
                    break

        self.editfeaturestack.append((form, feature))
        self.load_form(form)
        trigger_default_action()

    def clear_temp_objects(self):
        def clear_tool_band():
            """
            Clear the rubber band of the active tool if it has one
            """
            tool = self.canvas.mapTool()
            try:
                tool.clearBand()
            except AttributeError:
                # No clearBand method found, but that's cool.
                pass

        self.currentfeatureband.reset()
        clear_tool_band()


    def settings_updated(self, settings):
        self.actionGPS.updateGPSPort()
        gpslogging = settings.get('gpslogging', True)
        if self.gpslogging:
            self.gpslogging.logging = gpslogging

    def set_gps(self, gps, logging):
        self.gps = gps
        self.gpslogging = logging
        self.gps.gpsposition.connect(self.gps_update_canvas)
        self.gps.firstfix.connect(self.gps_first_fix)
        self.gps.gpsdisconnected.connect(self.gps_disconnected)

    def gps_update_canvas(self, position, gpsinfo):
        # Recenter map if we go outside of the 95% of the area
        if self.gpslogging.logging:
            self.gpsband.addPoint(position)
            self.gpsband.show()

        if roam.config.settings.get('gpscenter', True):
            if not self.lastgpsposition == position:
                self.lastposition = position
                rect = QgsRectangle(position, position)
                extentlimt = QgsRectangle(self.canvas.extent())
                extentlimt.scale(0.95)

                if not extentlimt.contains(position):
                    self.zoom_to_location(position)

        self.marker.show()
        self.marker.setCenter(position)

    def gps_first_fix(self, postion, gpsinfo):
        zoomtolocation = roam.config.settings.get('gpszoomonfix', True)
        if zoomtolocation:
            self.canvas.zoomScale(1000)
            self.zoom_to_location(postion)

    def zoom_to_location(self, position):
        rect = QgsRectangle(position, position)
        self.canvas.setExtent(rect)
        self.canvas.refresh()

    def gps_disconnected(self):
        self.marker.hide()

    def select_data_entry(self):
        def showformerror(form):
            pass

        def actions():
            for form in self.project.forms:
                action = form.createuiaction()
                valid, failreasons = form.valid
                if not valid:
                    roam.utils.warning("Form {} failed to load".format(form.label))
                    roam.utils.warning("Reasons {}".format(failreasons))
                    action.triggered.connect(partial(showformerror, form))
                else:
                    action.triggered.connect(partial(self.load_form, form))
                yield action

        formpicker = PickActionDialog(msg="Select data entry form")
        formpicker.addactions(actions())
        formpicker.exec_()

    def project_loaded(self, project):
        self.project = project
        self.actionPan.trigger()
        try:
            firstform = project.forms[0]
            self.load_form(firstform)
            self.dataentryselection.setVisible(True)
        except IndexError:
            self.dataentryselection.setVisible(False)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers)
        self.actionRaster.setEnabled(hasrasters)
        self.defaultextent = self.canvas.extent()
        roam.utils.info("Extent: {}".format(self.defaultextent.toString()))

        self.infoTool.selectionlayers = project.selectlayersmapping()

        self.canvas.freeze(False)
        self.canvas.refresh()

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def connectButtons(self):
        def connectAction(action, tool):
            action.toggled.connect(partial(self.setMapTool, tool))

        def cursor(name):
            pix = QPixmap(name)
            pix = pix.scaled(QSize(24,24))
            return QCursor(pix)

        self.zoomInTool = QgsMapToolZoom(self.canvas, False)
        self.zoomOutTool = QgsMapToolZoom(self.canvas, True)
        self.panTool = PanTool(self.canvas)
        self.infoTool = InfoTool(self.canvas)

        connectAction(self.actionZoom_In, self.zoomInTool)
        connectAction(self.actionZoom_Out, self.zoomOutTool)
        connectAction(self.actionPan, self.panTool)
        connectAction(self.actionInfo, self.infoTool)

        self.zoomInTool.setCursor(cursor(':/icons/in'))
        self.zoomOutTool.setCursor(cursor(':/icons/out'))
        self.infoTool.setCursor(cursor(':/icons/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(RoamEvents.selectionchanged.emit)

        self.actionHome.triggered.connect(self.homeview)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def load_form(self, form):
        self.clearCapatureTools()
        self.dataentryselection.setIcon(QIcon(form.icon))
        self.dataentryselection.setText(form.icontext)
        self.create_capture_buttons(form)

    def create_capture_buttons(self, form):
        tool = form.getMaptool()(self.canvas)
        for action in tool.actions:
            # Create the action here.
            if action.ismaptool:
                action.toggled.connect(partial(self.setMapTool, tool))

            # Set the action as a data entry button so we can remove it later.
            action.setProperty("dataentry", True)
            self.editgroup.addAction(action)
            self.layerbuttons.append(action)
            self.projecttoolbar.insertAction(self.topspaceraction, action)
            action.setChecked(action.isdefault)

        if hasattr(tool, 'geometryComplete'):
            add = partial(self.add_new_feature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showUIMessage, form.label))

    def add_new_feature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        # TODO Extract into function.
        # NOTE This function is doing too much, acts as add and also edit.
        layer = form.QGISLayer
        if layer.geometryType() in [QGis.WKBMultiLineString, QGis.WKBMultiPoint, QGis.WKBMultiPolygon]:
            geometry.convertToMultiType()

        try:
            form, feature = self.editfeaturestack.pop()
            self.editfeaturegeometry(form, feature, newgeometry=geometry)
            return
        except IndexError:
            pass

        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name() == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        RoamEvents.open_feature_form(form, feature, editmode=False)

    def editfeaturegeometry(self, form, feature, newgeometry):
        # TODO Extract into function.
        layer = form.QGISLayer
        layer.startEditing()
        feature.setGeometry(newgeometry)
        layer.updateFeature(feature)
        saved = layer.commitChanges()
        if not saved:
            map(roam.utils.error, layer.commitErrors())
        self.canvas.refresh()
        self.currentfeatureband.setToGeometry(feature.geometry(), layer)
        RoamEvents.editgeometry_complete.emit(form, feature)

    def clearCapatureTools(self):
        captureselected = False
        for action in self.projecttoolbar.actions():
            if action.objectName() == "capture" and action.isChecked():
                captureselected = True

            if action.property('dataentry'):
                self.projecttoolbar.removeAction(action)
        return captureselected

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
                # Really!? We have to reload the whole layer set every time?
            # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def cleanup(self):
        self.gpsband.reset()
        self.gpsband.hide()
        self.clear_selection()
        self.clear_temp_objects()
        self.clearCapatureTools()
        self.canvas.freeze()
        self.canvas.clear()
        self.canvas.freeze(False)
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)
class AdjustRasterMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rasterShadow = RasterShadowMapCanvasItem(self.canvas)

        self.rubberBandExtent = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandExtent.setColor(Qt.red)
        self.rubberBandExtent.setWidth(1)

        self.rubberBandAdjustSide = QgsRubberBand(
            self.canvas, QgsWkbTypes.LineGeometry)
        self.rubberBandAdjustSide.setColor(Qt.red)
        self.rubberBandAdjustSide.setWidth(3)

        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()
        self.layer = None

    def canvasPressEvent(self, e):
        # find the side of the rectangle closest to the click and some data
        # necessary to compute the new cneter and scale
        topLeft, topRight, bottomRight, bottomLeft = \
            self.layer.cornerCoordinates()
        top = [topLeft, topRight]
        right = [bottomRight, topRight]
        bottom = [bottomRight, bottomLeft]
        left = [bottomLeft, topLeft]

        click = QgsGeometry.fromPointXY(self.toMapCoordinates(e.pos()))

        # order is important (for referenceSide)
        sides = [top, right, bottom, left]
        distances = [click.distance(
            QgsGeometry.fromPolylineXY(side)) for side in sides]
        self.indexSide = self.minDistance(distances)
        self.side = sides[self.indexSide]
        self.sidePoint = self.center(self.side)
        self.vector = self.directionVector(self.side)
        # side that does not move (opposite of indexSide)
        self.referenceSide = sides[(self.indexSide + 2) % 4]
        self.referencePoint = self.center(self.referenceSide)
        self.referenceDistance = self.distance(
            self.sidePoint, self.referencePoint)
        self.isXScale = self.indexSide % 2 == 1

        self.startPoint = click.asPoint()
        self.endPoint = self.startPoint
        self.isEmittingPoint = True

        self.isLayerVisible = isLayerVisible(self.iface,
                                             self.layer)
        setLayerVisible(self.iface, self.layer, False)

        adjustment = self.computeAdjustment()
        self.showAdjustment(*adjustment)
        self.layer.history.append(
            {"action": "adjust", "center": self.layer.center, "xScale": self.layer.xScale, "yScale": self.layer.yScale})

    def minDistance(self, distances):
        sortedDistances = [i[0] for i in sorted(
            enumerate(distances), key=itemgetter(1))]
        # first is min
        return sortedDistances[0]

    def directionVector(self, side):
        sideCenter = self.center(side)
        layerCenter = self.layer.center
        vector = [sideCenter.x() - layerCenter.x(),
                  sideCenter.y() - layerCenter.y()]
        norm = math.sqrt(vector[0]**2 + vector[1]**2)
        normedVector = [vector[0] / norm, vector[1] / norm]
        return normedVector

    def center(self, side):
        return QgsPointXY((side[0].x() + side[1].x()) / 2,
                          (side[0].y() + side[1].y()) / 2)

    def distance(self, pt1, pt2):
        return math.sqrt((pt1.x() - pt2.x())**2 + (pt1.y() - pt2.y())**2)

    def canvasReleaseEvent(self, e):
        self.isEmittingPoint = False

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry)
        self.rasterShadow.reset()

        center, xScale, yScale = self.computeAdjustment()
        self.layer.setCenter(center)
        self.layer.setScale(xScale * self.layer.xScale,
                            yScale * self.layer.yScale)

        setLayerVisible(self.iface, self.layer,
                        self.isLayerVisible)
        self.layer.repaint()

        self.layer.commitTransformParameters()

    def canvasMoveEvent(self, e):
        if not self.isEmittingPoint:
            return

        self.endPoint = self.toMapCoordinates(e.pos())

        adjustment = self.computeAdjustment()
        self.showAdjustment(*adjustment)

    def computeAdjustment(self):
        dX = self.endPoint.x() - self.startPoint.x()
        dY = self.endPoint.y() - self.startPoint.y()
        # project on vector
        dp = dX * self.vector[0] + dY * self.vector[1]

        # do not go beyond 5% of the current size of side
        if dp < -0.95 * self.referenceDistance:
            dp = -0.95 * self.referenceDistance

        updatedSidePoint = QgsPointXY(self.sidePoint.x() + dp * self.vector[0],
                                      self.sidePoint.y() + dp * self.vector[1])

        center = self.center([self.referencePoint, updatedSidePoint])
        scaleFactor = self.distance(self.referencePoint, updatedSidePoint)
        if self.isXScale:
            xScale = scaleFactor / self.referenceDistance
            yScale = 1.0
        else:
            xScale = 1.0
            yScale = scaleFactor / self.referenceDistance

        return (center, xScale, yScale)

    def showAdjustment(self, center, xScale, yScale):
        _, rotation, originalXScale, originalYScale = \
            self.layer.transformParameters()
        newXScale = xScale * originalXScale
        newYScale = yScale * originalYScale
        cornerPoints = self.layer.transformedCornerCoordinates(
            center, rotation, newXScale, newYScale)

        self.rubberBandExtent.reset(QgsWkbTypes.LineGeometry)
        for point in cornerPoints:
            self.rubberBandExtent.addPoint(point, False)
        # for closing
        self.rubberBandExtent.addPoint(cornerPoints[0], True)
        self.rubberBandExtent.show()

        # show rubberband for side
        # see def of indexSide in init:
        # cornerpoints are (topLeft, topRight, bottomRight, bottomLeft)
        self.rubberBandAdjustSide.reset(QgsWkbTypes.LineGeometry)
        self.rubberBandAdjustSide.addPoint(
            cornerPoints[self.indexSide % 4], False)
        self.rubberBandAdjustSide.addPoint(
            cornerPoints[(self.indexSide + 1) % 4], True)
        self.rubberBandAdjustSide.show()

        self.rasterShadow.reset(self.layer)
        dx = center.x() - self.layer.center.x()
        dy = center.y() - self.layer.center.y()
        self.rasterShadow.setDeltaDisplacement(dx, dy, False)
        self.rasterShadow.setDeltaScale(xScale, yScale, True)
        self.rasterShadow.show()
class RectangleMapTool(QgsMapToolEmitPoint):

    rectangleCreated = pyqtSignal()
    deactivated = pyqtSignal()

    def __init__(self, canvas):
        self.canvas = canvas
        QgsMapToolEmitPoint.__init__(self, self.canvas)

        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
        self.rubberBand.setColor(QColor(255, 0, 0, 100))
        self.rubberBand.setWidth(2)

        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)

    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
        if self.rectangle() is not None:
            self.rectangleCreated.emit()

    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(QgsWkbTypes.PolygonGeometry)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return

        point1 = QgsPointXY(startPoint.x(), startPoint.y())
        point2 = QgsPointXY(startPoint.x(), endPoint.y())
        point3 = QgsPointXY(endPoint.x(), endPoint.y())
        point4 = QgsPointXY(endPoint.x(), startPoint.y())

        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        # True to update canvas
        self.rubberBand.addPoint(point4, True)
        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 setRectangle(self, rect):
        if rect == self.rectangle():
            return False

        if rect is None:
            self.reset()
        else:
            self.startPoint = QgsPointXY(rect.xMaximum(), rect.yMaximum())
            self.endPoint = QgsPointXY(rect.xMinimum(), rect.yMinimum())
            self.showRect(self.startPoint, self.endPoint)
        return True

    def deactivate(self):
        QgsMapTool.deactivate(self)
        self.deactivated.emit()
Example #31
0
class RectangleMapTool(QgsMapToolEmitPoint):

    def __init__(self, canvas):
        QgsMapToolEmitPoint.__init__(self, canvas)

        self.canvas = canvas
        self.rubberBand = QgsRubberBand(canvas, QGis.Polygon)
        self.rubberBand.setColor(QColor(255, 0, 0, 180))
        self.rubberBand.setWidth(1)
        self.reset()

    def reset(self):
        self.startPoint = self.endPoint = None
        self.isDrawing = False
        self.rubberBand.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        self.startPoint = self.toMapCoordinates(e.pos())
        self.endPoint = self.startPoint

        mapSettings = self.canvas.mapSettings(
        ) if QGis.QGIS_VERSION_INT >= 20300 else self.canvas.mapRenderer()
        self.mupp = mapSettings.mapUnitsPerPixel()
        self.rotation = mapSettings.rotation() if QGis.QGIS_VERSION_INT >= 20700 else 0

        self.isDrawing = True
        self.showRect(self.startPoint, self.endPoint)

    def canvasReleaseEvent(self, e):
        self.isDrawing = False
        self.emit(SIGNAL("rectangleCreated()"))

    def canvasMoveEvent(self, e):
        if not self.isDrawing:
            return
        self.endPoint = self.toMapCoordinates(e.pos())
        self.showRect(self.startPoint, self.endPoint)

    def showRect(self, startPoint, endPoint):
        self.rubberBand.reset(QGis.Polygon)
        if startPoint.x() == endPoint.x() and startPoint.y() == endPoint.y():
            return

        for i, pt in enumerate(self._rect(startPoint, endPoint).vertices()):
            self.rubberBand.addPoint(pt, bool(i == 3))
        self.rubberBand.show()

    def _rect(self, startPoint, endPoint):
        if startPoint is None or endPoint is None:
            return None

        p0 = self.toCanvasCoordinates(startPoint)
        p1 = self.toCanvasCoordinates(endPoint)
        canvas_rect = QgsRectangle(
            QgsPoint(
                p0.x(), p0.y()), QgsPoint(
                p1.x(), p1.y()))
        center = QgsPoint(
            (startPoint.x() + endPoint.x()) / 2,
            (startPoint.y() + endPoint.y()) / 2)
        return RotatedRect(center, self.mupp * canvas_rect.width(),
                           self.mupp * canvas_rect.height()).rotate(self.rotation, center)

    def rectangle(self):
        return self._rect(self.startPoint, self.endPoint)

    def setRectangle(self, rect):
        if rect == self._rect(self.startPoint, self.endPoint):
            return False

        v = rect.vertices()
        self.startPoint = v[3]
        self.endPoint = v[1]
        self.showRect(self.startPoint, self.endPoint)
        return True