class Start:
    def __init__(self, iface):
        self.name = "OPEN_FTTH"
        self.iface = iface
        self.autosave_enabled = False
        self.route_segment_layer = None
        self.route_node_layer = None
        self.websocket = BridgeWebsocket(self.iface)

        self.identifyHighlight = None
        self.last_identified_feature_mrid = None
        self.last_identified_feature_type = None

        self.event_handler = EventHandler(self.iface, self.websocket.websocket,
                                          self)
        self.websocket.messageReceived.connect(self.event_handler.handle)

        self.identifyNetworkElementHandler = IdentifyNetworkElementHandler(
            self.websocket)
        self.retrieve_selected_handler = RetrieveSelectedHandler(
            self.iface, self.websocket)
        self.application_settings = ApplicationSettings()
        self.layers_loaded = False

    def initGui(self):
        self.setupActions()
        self.iface.layerTreeView().currentLayerChanged.connect(
            self.layersLoaded)
        self.iface.layerTreeView().currentLayerChanged.connect(
            self.layerSelectionChange)

    def setupActions(self):
        self.actions = []

        icon_auto_save = ":/plugins/open_ftth/auto_save.svg"
        self.autosave_action = QAction(QtGui.QIcon(icon_auto_save), "Autosave",
                                       self.iface.mainWindow())
        self.autosave_action.setCheckable(True)
        self.autosave_action.triggered.connect(self.setupAutoSave)

        auto_identify = ":/plugins/open_ftth/auto_identify.svg"
        self.select_action = QAction(QtGui.QIcon(auto_identify), "Select",
                                     self.iface.mainWindow())
        self.select_action.setCheckable(True)
        self.select_action.triggered.connect(self.setupSelectTool)

        web_browser = ":/plugins/open_ftth/browser_icon.svg"
        self.web_browser_action = QAction(QtGui.QIcon(web_browser),
                                          "Web-browser",
                                          self.iface.mainWindow())
        self.web_browser_action.setCheckable(False)
        self.web_browser_action.triggered.connect(self.connectWebBrowser)

        paste_geometry = ":/plugins/open_ftth/paste_geometry.svg"
        self.paste_geometry_action = QAction(QtGui.QIcon(paste_geometry),
                                             "Paste geometry",
                                             self.iface.mainWindow())
        self.paste_geometry_action.setCheckable(False)
        self.paste_geometry_action.triggered.connect(self.pasteGeometry)

        self.iface.addPluginToMenu("&OPEN FTTH", self.select_action)
        self.iface.addToolBarIcon(self.autosave_action)
        self.iface.addToolBarIcon(self.select_action)
        self.iface.addToolBarIcon(self.web_browser_action)
        self.iface.addToolBarIcon(self.paste_geometry_action)

        self.identify_tool = IdentifySelect(self.iface.mapCanvas())
        self.identify_tool.identified.connect(self.onIdentified)
        self.identify_tool.identifiedNone.connect(self.onIdentifiedNone)
        self.buildActionListIdentifyTool()

    def buildActionListIdentifyTool(self):
        actionList = self.iface.mapNavToolToolBar().actions()

        # Add actions from QGIS attributes toolbar (handling QWidgetActions)
        tmpActionList = self.iface.attributesToolBar().actions()
        for action in tmpActionList:
            if isinstance(action, QWidgetAction):
                actionList.extend(action.defaultWidget().actions())
            else:
                actionList.append(action)

        tmpActionList = self.iface.digitizeToolBar().actions()
        for action in tmpActionList:
            if isinstance(action, QWidgetAction):
                actionList.extend(action.defaultWidget().actions())
            else:
                actionList.append(action)

        tmpActionList = self.iface.selectionToolBar().actions()
        for action in tmpActionList:
            if isinstance(action, QWidgetAction):
                actionList.extend(action.defaultWidget().actions())
            else:
                actionList.append(action)

        # Build a group with actions from actionList and add your own action
        group = QActionGroup(self.iface.mainWindow())
        group.setExclusive(True)
        for action in actionList:
            group.addAction(action)
        group.addAction(self.select_action)

    def unload(self):
        for action in self.actions:
            self.iface.removeToolBarIcon(action)

        self.autosave_action.setChecked(False)
        self.select_action.setChecked(False)

        self.autosave_enabled = False
        self.select_tool_enabled = False

        try:
            self.disconnectSelectedFeatures()
            self.disconnectSelectTool()
            self.websocket.close()
            self.route_segment_layer.beforeCommitChanges.disconnect(
                self.preCommitDeleteHandlerRouteSegment)
            self.route_node_layer.beforeCommitChanges.disconnect(
                self.preCommitDeleteHandlerRouteNode)
            self.route_node_layer.featuresDeleted.disconnect(
                self.checkFeaturesDeleted)
            self.route_segment_layer.featuresDeleted.disconnect(
                self.checkFeaturesDeleted)
        except Exception:
            pass

    def setupSelectTool(self):
        self.iface.mapCanvas().setMapTool(self.identify_tool)

    def sendSelectedFeatures(self):
        self.getSelectedFeaturesHandler.handle()

    def setupAutoSave(self):
        if self.autosave_enabled is False:
            self.connectAutosave()
            self.saveActiveLayerEdits()
        else:
            self.disconnectAutosave()

    def layersLoaded(self):
        if not self.hasCorrectLayers():
            self.onIdentifiedNone()
            return

        if self.layers_loaded is False:
            self.websocket.start()
            self.layers_loaded = True
            self.route_segment_layer = QgsProject.instance().mapLayersByName(
                ApplicationSettings().get_layers_route_segment_name())[0]
            self.route_node_layer = QgsProject.instance().mapLayersByName(
                ApplicationSettings().get_layers_route_node_name())[0]

            try:
                self.route_node_layer.featuresDeleted.connect(
                    self.checkFeaturesDeleted)
                self.route_segment_layer.featuresDeleted.connect(
                    self.checkFeaturesDeleted)
            except TypeError:
                pass

            try:
                self.route_segment_layer.beforeCommitChanges.connect(
                    self.preCommitDeleteHandlerRouteSegment)
                self.route_node_layer.beforeCommitChanges.connect(
                    self.preCommitDeleteHandlerRouteNode)
            except TypeError:
                pass

    def layerSelectionChange(self):
        if not self.hasCorrectLayers():
            self.onIdentifiedNone()
            return

        self.route_segment_layer = QgsProject.instance().mapLayersByName(
            ApplicationSettings().get_layers_route_segment_name())[0]
        try:
            self.route_segment_layer.selectionChanged.disconnect(
                self.onSelectedSegment)
        except TypeError:
            pass

        self.route_segment_layer.selectionChanged.connect(
            self.onSelectedSegment)

    def hasCorrectLayers(self):
        route_segment_layers = QgsProject.instance().mapLayersByName(
            ApplicationSettings().get_layers_route_segment_name())
        route_node_layers = QgsProject.instance().mapLayersByName(
            ApplicationSettings().get_layers_route_node_name())
        return len(route_segment_layers) > 0 and len(route_node_layers) > 0

    def connectAutosave(self):
        # We do this to avoid plugin crash in case that connects come in an invalid state.
        try:
            self.route_segment_layer = QgsProject.instance().mapLayersByName(
                ApplicationSettings().get_layers_route_segment_name())[0]
            self.route_segment_layer.layerModified.connect(
                self.saveActiveLayerEdits)
        except TypeError:
            pass

        # We do this to avoid plugin crash in case that connects come in an invalid state.
        try:
            self.route_node_layer = QgsProject.instance().mapLayersByName(
                ApplicationSettings().get_layers_route_node_name())[0]
            self.route_node_layer.layerModified.connect(
                self.saveActiveLayerEdits)
        except TypeError:
            pass

        self.autosave_enabled = True

    def disconnectAutosave(self):
        # We do this to avoid plugin crash in case that connects come in an invalid state.
        try:
            self.route_segment_layer.layerModified.disconnect(
                self.saveActiveLayerEdits)
        except TypeError:
            pass

        # We do this to avoid plugin crash in case that connects come in an invalid state.
        try:
            self.route_node_layer.layerModified.disconnect(
                self.saveActiveLayerEdits)
        except TypeError:
            pass

        self.autosave_action.setChecked(False)
        self.autosave_enabled = False

    def connectWebBrowser(self):
        webbrowser.open(self.application_settings.get_website_url(), new=2)

    def saveActiveLayerEdits(self):
        self.iface.actionSaveActiveLayerEdits().trigger()

    def onSelectedSegment(self):
        message = type(
            'Expando', (object, ),
            {'username': self.application_settings.get_user_name_prefix()})()
        self.retrieve_selected_handler.handle(message)

    def checkFeaturesDeleted(self, fids):
        if self.last_identified_feature_mrid is None:
            return

        features = None
        filterExpression = f'"mrid" = \'{self.last_identified_feature_mrid}\''
        if self.last_identified_feature_type == self.application_settings.get_types_route_node(
        ):
            features = self.route_node_layer.getFeatures(
                QgsFeatureRequest().setFilterExpression(filterExpression))
        elif self.last_identified_feature_type == self.application_settings.get_types_route_segment(
        ):
            features = self.route_segment_layer.getFeatures(
                QgsFeatureRequest().setFilterExpression(filterExpression))
        else:
            return

        # hack because the deleted signal is triggered on both create and delete
        stillExists = False
        for feature in features:
            stillExists = True

        if not stillExists:
            self.onIdentifiedNone()
            self.identifyNetworkElementHandler.handle(None, None)
        # end of hack

    def onIdentified(self, selected_layer, selected_feature):
        selected_type = ""
        if self.application_settings.get_layers_route_node_name(
        ) == selected_layer.sourceName():
            selected_type = self.application_settings.get_types_route_node()
        elif self.application_settings.get_layers_route_segment_name(
        ) == selected_layer.sourceName():
            selected_type = self.application_settings.get_types_route_segment()
        else:
            self.identifyHighlight.hide()
            return

        if self.identifyHighlight is not None:
            self.identifyHighlight.hide()

        color = QColor(0, 255, 0)
        self.identifyHighlight = QgsHighlight(self.iface.mapCanvas(),
                                              selected_feature.geometry(),
                                              selected_layer)
        self.identifyHighlight.setWidth(3)
        self.identifyHighlight.setColor(color)
        self.identifyHighlight.show()

        mrid = selected_feature.attribute("mrid")

        self.last_identified_feature_mrid = mrid
        self.last_identified_feature_type = selected_type

        if selected_type != "":
            self.identifyNetworkElementHandler.handle(mrid, selected_type)

    def onIdentifiedNone(self):
        if self.identifyHighlight is not None:
            self.last_identified_feature_mrid = None
            self.last_identified_feature_type = None
            self.identifyHighlight.hide()

    def preCommitDeleteHandlerRouteSegment(self):
        self.undoDeleteSetMarkedToBeDeleted(
            ApplicationSettings().get_layers_route_segment_name())

    def preCommitDeleteHandlerRouteNode(self):
        self.undoDeleteSetMarkedToBeDeleted(
            ApplicationSettings().get_layers_route_node_name())

    def undoDeleteSetMarkedToBeDeleted(self, layerName):
        layers = QgsProject.instance().mapLayersByName(layerName)
        if len(layers) != 1:
            return
        layer = layers[0]

        deleted_features_ids = layer.editBuffer().deletedFeatureIds()
        for feature_id in deleted_features_ids:
            QgsVectorLayerUndoCommandDeleteFeature(layer.editBuffer(),
                                                   feature_id).undo()

        marked_to_be_deleted_idx = layer.fields().indexOf(
            'marked_to_be_deleted')
        user_name_idx = layer.fields().indexOf('user_name')
        user_name = self.application_settings.get_user_name()
        for feature in layer.dataProvider().getFeatures(
                QgsFeatureRequest().setFilterFids(deleted_features_ids)):
            layer.changeAttributeValue(feature.id(), marked_to_be_deleted_idx,
                                       True)
            layer.changeAttributeValue(feature.id(), user_name_idx, user_name)

    def pasteGeometry(self):
        layer = self.iface.activeLayer()

        route_segment_layer_name = self.application_settings.get_layers_route_segment_name(
        )
        if layer.sourceName() != route_segment_layer_name:
            self.showBarMessage(
                "You can only paste a geometry when layer %s is selected." %
                route_segment_layer_name, Qgis.Warning)
            return

        if not layer.isEditable():
            self.showBarMessage(
                "You need to be in edit mode to paste the geometry.",
                Qgis.Warning)
            return

        geoms = self.tryGetFeaturesGeomsFromClipBoard()
        if len(geoms) > 1:
            self.showBarMessage(
                "Can't paste geometry multiple features in clipboard.",
                Qgis.Warning)
            return

        if len(geoms) == 0:
            self.showBarMessage(
                "Can't paste geometry. No features in clipboard.",
                Qgis.Warning)
            return

        selected_features_iter = layer.getSelectedFeatures()
        selected_features = []
        for selected_feature in selected_features_iter:
            selected_features.append(selected_feature)

        if len(selected_features) == 0:
            self.showBarMessage("Can't paste. No target feature to paste to.",
                                Qgis.Warning)
            return

        # Paste is the comment we want to paste to
        paste_feature = selected_features[0]
        paste_geom = paste_feature.geometry()

        # Copy is the geom from the clipboard that we want "paste" to resemble.
        copy_geom = geoms[0]

        if paste_geom.type() != copy_geom.type():
            self.showBarMessage(
                "Not the same geometry type. From %s to %s" %
                (copy_geom.type(), paste_geom.type()), Qgis.Warning)
            return

        paste_geom_start = paste_geom.asPolyline()[0]
        paste_geom_end = paste_geom.asPolyline()[len(paste_geom.asPolyline()) -
                                                 1]
        copy_geom_start = copy_geom.asPolyline()[0]
        copy_geom_end = copy_geom.asPolyline()[len(copy_geom.asPolyline()) - 1]

        start_to_start_distance = paste_geom_start.distance(copy_geom_start)
        start_to_end_distance = paste_geom_start.distance(copy_geom_end)

        new_copy_polyline = copy_geom.asPolyline()

        if start_to_start_distance > start_to_end_distance:
            QgsMessageLog.logMessage(
                "The geometries are flipped, we reverse them for the copy.",
                self.name, Qgis.Info)
            new_copy_polyline.reverse()

        # Its important that we do this after, in case that the geometry is reversed.
        new_copy_geom_start = new_copy_polyline[0]
        new_copy_geom_end = new_copy_polyline[len(new_copy_polyline) - 1]

        new_start_to_start_distance = paste_geom_start.distance(
            new_copy_geom_start)
        if self.application_settings.get_tolerance(
        ) < new_start_to_start_distance:
            self.showBarMessage(
                "Start point distance is bigger than tolerance.",
                Qgis.Critical)
            return

        new_start_to_end_distance = paste_geom_end.distance(new_copy_geom_end)
        if self.application_settings.get_tolerance(
        ) < new_start_to_end_distance:
            self.showBarMessage(
                "End points distance is bigger than tolerance.", Qgis.Critical)
            return

        # We update the geometry.
        result = layer.changeGeometry(
            paste_feature.id(), QgsGeometry.fromPolylineXY(new_copy_polyline))

        # We change the mapping method to LandSurveying to identify that the polyline now matches the landsurvey polyline.
        mapping_method_idx = layer.fields().indexOf('mapping_method')
        layer.changeAttributeValue(paste_feature.id(), mapping_method_idx,
                                   "LandSurveying")

        if not result:
            self.showBarMessage("Can't paste geometry, something went wrong.",
                                Qgis.Critical)
            return

        self.iface.mapCanvas().refresh()

    def tryGetFeaturesGeomsFromClipBoard(self):
        cb = QApplication.clipboard()
        clipboard_text = cb.text()
        if sys.version_info[0] == 2:
            clipboard_text = clipboard_text.encode('utf-8')

        reader = csv.DictReader(StringIO(clipboard_text), delimiter='\t')

        geoms = []
        for row in reader:
            wkt_geom = row.get('wkt_geom')
            geom = QgsGeometry.fromWkt(wkt_geom)

            if not geom:
                self.showBarMessage(
                    'Can\'t create geometry from wkt: %s' % wkt_geom,
                    Qgis.Critical)
                return []

            geoms.append(geom)
        return geoms

    def showBarMessage(self, message, level=Qgis.Info, duration=-1):
        self.iface.messageBar().pushMessage("Error",
                                            message,
                                            level=level,
                                            duration=duration)
Beispiel #2
0
class FeatureSelectorWidget(QWidget):
    feature_identified = pyqtSignal(QgsFeature)

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        edit_layout = QHBoxLayout()
        edit_layout.setContentsMargins(0, 0, 0, 0)
        edit_layout.setSpacing(2)
        self.setLayout(edit_layout)

        self.line_edit = QLineEdit(self)
        self.line_edit.setReadOnly(True)
        edit_layout.addWidget(self.line_edit)

        self.highlight_feature_button = QToolButton(self)
        self.highlight_feature_button.setPopupMode(QToolButton.MenuButtonPopup)
        self.highlight_feature_action = QAction(
            QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"),
            "Highlight feature", self)
        self.scale_highlight_feature_action = QAction(
            QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"),
            "Scale and highlight feature", self)
        self.pan_highlight_feature_action = QAction(
            QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"),
            "Pan and highlight feature", self)
        self.highlight_feature_button.addAction(self.highlight_feature_action)
        self.highlight_feature_button.addAction(
            self.scale_highlight_feature_action)
        self.highlight_feature_button.addAction(
            self.pan_highlight_feature_action)
        self.highlight_feature_button.setDefaultAction(
            self.highlight_feature_action)
        edit_layout.addWidget(self.highlight_feature_button)

        self.map_identification_button = QToolButton(self)
        self.map_identification_button.setIcon(
            QgsApplication.getThemeIcon("/mActionMapIdentification.svg"))
        self.map_identification_button.setText("Select on map")
        self.map_identification_button.setCheckable(True)
        edit_layout.addWidget(self.map_identification_button)

        self.map_identification_button.clicked.connect(self.map_identification)
        self.highlight_feature_button.triggered.connect(
            self.highlight_action_triggered)

        self.layer = None
        self.map_tool = None
        self.canvas = None
        self.window_widget = None
        self.highlight = None
        self.feature = QgsFeature()

    def set_canvas(self, map_canvas):
        self.map_tool = QgsMapToolIdentifyFeature(map_canvas)
        self.map_tool.setButton(self.map_identification_button)
        self.canvas = map_canvas

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

    def set_feature(self, feature, canvas_extent=CanvasExtent.Fixed):
        self.line_edit.clear()
        self.feature = feature

        if self.feature is None or not self.feature.isValid(
        ) or self.layer is None:
            return

        expression = QgsExpression(self.layer.displayExpression())
        context = QgsExpressionContext()
        scope = QgsExpressionContextScope()
        context.appendScope(scope)
        scope.setFeature(feature)
        feature_title = expression.evaluate(context)
        if feature_title == "":
            feature_title = feature.id()
        self.line_edit.setText(str(feature_title))
        self.highlight_feature(canvas_extent)

    def clear(self):
        self.feature = QgsFeature()
        self.line_edit.clear()

    @pyqtSlot()
    def map_identification(self):
        if self.layer is None or self.map_tool is None or self.canvas is None:
            return

        self.map_tool.setLayer(self.layer)
        self.canvas.setMapTool(self.map_tool)

        self.window_widget = QWidget.window(self)
        self.canvas.window().raise_()
        self.canvas.activateWindow()
        self.canvas.setFocus()

        self.map_tool.featureIdentified.connect(
            self.map_tool_feature_identified)
        self.map_tool.deactivated.connect(self.map_tool_deactivated)

    def map_tool_feature_identified(self, feature):
        feature = QgsFeature(feature)
        self.feature_identified.emit(feature)
        self.unset_map_tool()
        self.set_feature(feature)

    def map_tool_deactivated(self):
        if self.window_widget is not None:
            self.window_widget.raise_()
            self.window_widget.activateWindow()

    def highlight_feature(self, canvas_extent=CanvasExtent.Fixed):
        if self.canvas is None or not self.feature.isValid():
            return

        geom = self.feature.geometry()

        if geom is None:
            return

        if canvas_extent == CanvasExtent.Scale:
            feature_bounding_box = geom.boundingBox()
            feature_bounding_box = self.canvas.mapSettings(
            ).layerToMapCoordinates(self.layer, feature_bounding_box)
            extent = self.canvas.extent()
            if not extent.contains(feature_bounding_box):
                extent.combineExtentWith(feature_bounding_box)
                extent.scale(1.1)
                self.canvas.setExtent(extent)
                self.canvas.refresh()

        elif canvas_extent == CanvasExtent.Pan:
            centroid = geom.centroid()
            center = centroid.asPoint()

            center = self.canvas.mapSettings().layerToMapCoordinates(
                self.layer, center)
            self.canvas.zoomByFactor(1.0,
                                     center)  # refresh is done in this method

        # highlight
        self.delete_highlight()
        self.highlight = QgsHighlight(self.canvas, geom, self.layer)

        settings = QSettings()
        color = QColor(
            settings.value("/Map/highlight/color",
                           Qgis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = int(
            settings.value("/Map/highlight/colorAlpha",
                           Qgis.DEFAULT_HIGHLIGHT_COLOR.alpha()))
        buffer = 2 * float(
            settings.value("/Map/highlight/buffer",
                           Qgis.DEFAULT_HIGHLIGHT_BUFFER_MM))
        min_width = 2 * float(
            settings.value("/Map/highlight/min_width",
                           Qgis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM))

        self.highlight.setColor(color)  # sets also fill with default alpha
        color.setAlpha(alpha)
        self.highlight.setFillColor(color)  # sets fill with alpha
        self.highlight.setBuffer(buffer)
        self.highlight.setMinWidth(min_width)
        self.highlight.setWidth(4.0)
        self.highlight.show()

        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.delete_highlight)
        self.timer.start(3000)

    def delete_highlight(self):
        if self.highlight is not None:
            self.highlight.hide()
            del self.highlight
            self.highlight = None

    def unset_map_tool(self):
        if self.canvas is not None and self.map_tool is not None:
            # this will call mapTool.deactivated
            self.canvas.unsetMapTool(self.map_tool)

    def highlight_action_triggered(self, action):
        self.highlight_feature_button.setDefaultAction(action)

        if action == self.highlight_feature_action:
            self.highlight_feature()

        elif action == self.scale_highlight_feature_action:
            self.highlight_feature(CanvasExtent.Scale)

        elif action == self.pan_highlight_feature_action:
            self.highlight_feature(CanvasExtent.Pan)
class FeatureSelectorWidget(QWidget):
    featureIdentified = pyqtSignal(QgsFeature)

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        editLayout = QHBoxLayout()
        editLayout.setContentsMargins(0, 0, 0, 0)
        editLayout.setSpacing(2)
        self.setLayout(editLayout)

        self.lineEdit = QLineEdit(self)
        self.lineEdit.setReadOnly(True)
        editLayout.addWidget(self.lineEdit)
        
        self.highlightFeatureButton = QToolButton(self)
        self.highlightFeatureButton.setPopupMode(QToolButton.MenuButtonPopup)
        self.highlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"), "Highlight feature", self)
        self.scaleHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"), "Scale and highlight feature", self)
        self.panHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"), "Pan and highlight feature", self)
        self.highlightFeatureButton.addAction(self.highlightFeatureAction)
        self.highlightFeatureButton.addAction(self.scaleHighlightFeatureAction)
        self.highlightFeatureButton.addAction(self.panHighlightFeatureAction)
        self.highlightFeatureButton.setDefaultAction(self.highlightFeatureAction)
        editLayout.addWidget(self.highlightFeatureButton)

        self.mapIdentificationButton = QToolButton(self)
        self.mapIdentificationButton.setIcon(QgsApplication.getThemeIcon("/mActionMapIdentification.svg"))
        self.mapIdentificationButton.setText("Select on map")
        self.mapIdentificationButton.setCheckable(True)
        editLayout.addWidget(self.mapIdentificationButton)

        self.mapIdentificationButton.clicked.connect(self.mapIdentification)
        self.highlightFeatureButton.triggered.connect(self.highlightActionTriggered)

        self.layer = None
        self.mapTool = None
        self.canvas = None
        self.windowWidget = None
        self.highlight = None
        self.feature = QgsFeature()

    def setCanvas(self, mapCanvas):
        self.mapTool = QgsMapToolIdentifyFeature(mapCanvas)
        self.mapTool.setButton(self.mapIdentificationButton)
        self.canvas = mapCanvas

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

    def setFeature(self, feature, canvasExtent = CanvasExtent.Fixed):
        self.lineEdit.clear()
        self.feature = feature

        if not self.feature.isValid() or self.layer is None:
            return

        featureTitle = feature.attribute(self.layer.displayField())
        if featureTitle == '':
            featureTitle = feature.id()
        self.lineEdit.setText(str(featureTitle))
        self.highlightFeature(canvasExtent)


    def clear(self):
        self.feature = QgsFeature()
        self.lineEdit.clear()

    def mapIdentification(self):
        if self.layer is None or self.mapTool is None or self.canvas is None:
            return

        self.mapTool.setLayer(self.layer)
        self.canvas.setMapTool(self.mapTool)

        self.windowWidget = QWidget.window(self)
        self.canvas.window().raise_()
        self.canvas.activateWindow()
        self.canvas.setFocus()

        self.mapTool.featureIdentified.connect(self.mapToolFeatureIdentified)
        self.mapTool.deactivated.connect(self.mapToolDeactivated)

    def mapToolFeatureIdentified(self, feature):
        feature = QgsFeature(feature)
        self.featureIdentified.emit(feature)
        self.unsetMapTool()
        self.setFeature(feature)

    def mapToolDeactivated(self):
        if self.windowWidget is not None:
            self.windowWidget.raise_()
            self.windowWidget.activateWindow()

    def highlightFeature(self, canvasExtent = CanvasExtent.Fixed):
        if self.canvas is None or not self.feature.isValid():
            return

        geom = self.feature.geometry()

        if geom is None:
            return
  
        if canvasExtent == CanvasExtent.Scale:
            featBBox = geom.boundingBox()
            featBBox = self.canvas.mapSettings().layerToMapCoordinates(self.layer, featBBox)
            extent = self.canvas.extent()
            if not extent.contains(featBBox):
                extent.combineExtentWith(featBBox)
                extent.scale(1.1)
                self.canvas.setExtent(extent)
                self.canvas.refresh()
            
        elif canvasExtent == CanvasExtent.Pan:
            centroid = geom.centroid()
            center = centroid.asPoint()

            center = self.canvas.mapSettings().layerToMapCoordinates(self.layer, center)
            self.canvas.zoomByFactor(1.0, center)  # refresh is done in this method

        # highlight
        self.delete_highlight()
        self.highlight = QgsHighlight(self.canvas, geom, self.layer)

        settings = QSettings()
        color = QColor(settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha()))
        buffer = 2*float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM))
        min_width = 2*float(settings.value("/Map/highlight/min_width", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM))

        self.highlight.setColor(color)  # sets also fill with default alpha
        color.setAlpha(alpha)
        self.highlight.setFillColor(color)  # sets fill with alpha
        self.highlight.setBuffer(buffer)
        self.highlight.setMinWidth(min_width)
        self.highlight.setWidth(4.0)
        self.highlight.show()
        
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.delete_highlight)
        self.timer.start(3000)

    def delete_highlight(self):
        if self.highlight is not None:
            self.highlight.hide()
            del self.highlight
            self.highlight = None

    def unsetMapTool(self):
        if self.canvas is not None and self.mapTool is not None:
            # this will call mapToolDeactivated
            self.canvas.unsetMapTool(self.mapTool)

    def highlightActionTriggered(self, action):
        self.highlightFeatureButton.setDefaultAction(action)

        if action == self.highlightFeatureAction:
            self.highlightFeature()

        elif action == self.scaleHighlightFeatureAction:
            self.highlightFeature(CanvasExtent.Scale)

        elif action == self.panHighlightFeatureAction:
            self.highlightFeature(CanvasExtent.Pan)