def getSnapRubberBand(self):
     rubberBand = QgsRubberBand(self.canvas, geometryType = QGis.Point)
     rubberBand.setFillColor(QColor(255, 0, 0, 40))
     rubberBand.setBorderColor(QColor(255, 0, 0, 200))
     rubberBand.setWidth(2)
     rubberBand.setIcon(QgsRubberBand.ICON_X)
     return rubberBand        
 def getSnapRubberBand(self):
     rubberBand = QgsRubberBand(self.canvas, geometryType = QGis.Point)
     rubberBand.setFillColor(QColor(255, 0, 0, 40))
     rubberBand.setBorderColor(QColor(255, 0, 0, 200))
     rubberBand.setWidth(2)
     rubberBand.setIcon(QgsRubberBand.ICON_X)
     return rubberBand        
Example #3
0
 def startFlashFeature(self, featureGeometry, layerCrs):
     geomType = QgsWkbTypes.geometryType(featureGeometry.wkbType())
     rb = QgsRubberBand(iface.mapCanvas(), geomType)
     rb.addGeometry( featureGeometry, layerCrs )
     flashes=3
     duration=500
     if geomType == QgsWkbTypes.LineGeometry or geomType == QgsWkbTypes.PointGeometry:
         rb.setWidth( 2 )
         rb.setSecondaryStrokeColor( QColor( 255, 255, 255 ) )
     if geomType == QgsWkbTypes.PointGeometry :
         rb.setIcon( QgsRubberBand.ICON_CIRCLE )
     startColor = QColor(255, 0, 0, 255)
     startColor.setAlpha( 255 )
     endColor = QColor(255, 0, 0, 0)
     endColor.setAlpha( 0 )
     self.animation = QVariantAnimation( iface.mapCanvas() )
     self.animation.finished.connect(lambda a=self.animation, b=rb: self.finishedAnimation(a, b))
     self.animation.valueChanged.connect(lambda value, a=self.animation, b=rb, c=geomType: self.valueChangedAnimation(value, a, b, c))
     self.animation.setDuration( duration * flashes )
     self.animation.setStartValue( endColor )
     midStep = 0.2 / flashes
     for i in range(flashes):
         start = float(i  / flashes)
         self.animation.setKeyValueAt( start + midStep, startColor )
         end = float(( i + 1 ) / flashes)
         if not(end == 1.0):
             self.animation.setKeyValueAt( end, endColor )
     self.animation.setEndValue( endColor )
     self.animation.start()
Example #4
0
 def getSnapRubberBand(self):
     rubberBand = QgsRubberBand(self.canvas,
                                geometryType=QgsWkbTypes.PointGeometry)
     rubberBand.setFillColor(QColor(255, 0, 0, 40))
     rubberBand.setSecondaryStrokeColor(QColor(255, 0, 0, 200))
     rubberBand.setWidth(2)
     rubberBand.setIcon(QgsRubberBand.ICON_X)
     return rubberBand
Example #5
0
class Create_points(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        super(Create_points, self).__init__(canvas)
        self.points = QgsRubberBand(canvas, QgsWkbTypes.PointGeometry)

    def create_points_window(self):
        self.crt_pts_win = CreatePointsWindow()
        self.crt_pts_win.show()

    def canvasClicked(self, przycisk, punkt):
        print(przycisk, punkt)

    def deactivate(self):
        super(Create_points, self).deactivate()

    def canvasReleaseEvent(self, mouse_event):
        point = mouse_event.mapPoint()
        #print(point)
        dada = self.points.addPoint(point)
        #print(dada)
        self.points.setWidth(5)
        self.points.setIconSize(20)
        self.points.setIcon(QgsRubberBand.ICON_BOX)
        self.points.setColor(QColor('red'))
class wincan2qgep(QObject):

    name = u"&Wincan 2 QGEP"
    actions = None

    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.actions = {}
        self.settings = MySettings()
        self.dlg = None

        # translation environment
        self.plugin_dir = os.path.dirname(__file__)
        locale = QSettings().value("locale/userLocale")[0:2]
        localePath = os.path.join(self.plugin_dir, 'i18n', 'wincan2qgep_{0}.qm'.format(locale))
        if os.path.exists(localePath):
            self.translator = QTranslator()
            self.translator.load(localePath)
            QCoreApplication.installTranslator(self.translator)

    def initGui(self):
        self.actions['openInspection'] = QAction(
            QIcon(":/plugins/wincan2qgep/icons/wincan_logo.png"),
            self.tr(u"Ouvrir une inspection"),
            self.iface.mainWindow())
        self.actions['openInspection'].triggered.connect(self.openInspection)
        self.iface.addPluginToMenu(self.name, self.actions['openInspection'])
        self.iface.addToolBarIcon(self.actions['openInspection'])

        self.actions['showSettings'] = QAction(
            QIcon(":/plugins/wincan2qgep/icons/settings.svg"),
            self.tr(u"&Settings"),
            self.iface.mainWindow())
        self.actions['showSettings'].triggered.connect(self.showSettings)
        self.iface.addPluginToMenu(self.name, self.actions['showSettings'])

        self.actions['help'] = QAction(
            QIcon(":/plugins/wincan2qgep/icons/help.svg"),
            self.tr("Help"),
            self.iface.mainWindow())
        self.actions['help'].triggered.connect(lambda: QDesktopServices().openUrl(QUrl("http://3nids.github.io/wincan2qgep")))
        self.iface.addPluginToMenu(self.name, self.actions['help'])

        self.rubber = QgsRubberBand(self.iface.mapCanvas())
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

    def unload(self):
        """ Unload plugin """
        for action in self.actions.itervalues():
            self.iface.removePluginMenu(self.name, action)
            self.iface.removeToolBarIcon(action)
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber
        if self.dlg:
            self.dlg.close()

    @pyqtSlot(str, QgsMessageBar.MessageLevel)
    def displayMessage(self, message, level):
        self.iface.messageBar().pushMessage("Wincan 2 QGEP", message, level)

    def showSettings(self):
        if ConfigurationDialog().exec_():
            self._reloadFinders()

    def openInspection(self):
        xmlPath = self.settings.value('xmlPath')
        if xmlPath == '':
            xmlPath = QgsProject.instance().homePath()
        filepath = QFileDialog.getOpenFileName(None, "Open WIncan inspection data", xmlPath, "Wincan file (*.xml)")
        #filepath = '/var/run/user/1000/gvfs/smb-share:server=s4laveyre.sige.ch,share=inspection_tv/SIGE_2014/Rapport 2014/SIGE 5004B 14/XML/Project.xml'

        if filepath:
            self.settings.set_value('xmlPath', os.path.dirname(os.path.realpath(filepath)))
            data = ImportData(filepath).data
            self.dlg = DataBrowserDialog(self.iface, data)
            self.dlg.show()
class DiviPluginHistoryDialog(QDialog, FORM_CLASS):

    def __init__(self, plugin, parent=None):
        """Constructor."""
        super(DiviPluginHistoryDialog, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.plugin = plugin
        #self.iface = plugin.iface
        
        self.setupUi(self)
        self.initGui()
    
    def initGui(self):
        #Models
        self.tblChanges.setModel( ChangeModel() )
        proxyChanges = HistoryProxyModel()
        proxyChanges.setSourceModel( HistoryModel() )
        self.tblHistory.setModel( proxyChanges )
        #Signals
        self.plugin.tvIdentificationResult.model().sourceModel().on_history.connect( self.historyChanged )
        self.tblHistory.selectionModel().currentChanged.connect( self.currentHistoryChanged )
        #Widgets
        settings = QSettings()
        self.mapCanvas = QgsMapCanvas(self.vSplitter)
        self.mapCanvas.setDestinationCrs( QgsCoordinateReferenceSystem('EPSG:4326') )
        zoomFactor = settings.value( "/qgis/zoom_factor", 2.0, type=float )
        action = settings.value( "/qgis/wheel_action", 0, type=int)
        self.mapCanvas.setWheelFactor( zoomFactor )
        self.mapCanvas.enableAntiAliasing( settings.value( "/qgis/enable_anti_aliasing", False, type=bool ))
        #self.mapCanvas.useImageToRender( settings.value( "/qgis/use_qimage_to_render", False, type=bool ))
        self.toolPan = QgsMapToolPan( self.mapCanvas )
        self.mapCanvas.setMapTool( self.toolPan )
        #Canvas items
        self.new_geometry = QgsRubberBand(self.mapCanvas)
        self.new_geometry.setWidth(2)
        self.new_geometry.setIcon( QgsRubberBand.ICON_CIRCLE )
        g = QColor(0, 128, 0, 100)
        self.new_geometry.setColor( g )
        self.old_geometry = QgsRubberBand(self.mapCanvas)
        self.old_geometry.setWidth(2)
        self.old_geometry.setIcon( QgsRubberBand.ICON_CIRCLE )
        r = QColor(255, 0, 0, 100)
        self.old_geometry.setColor( r )
    
    def show(self, data=[]):
        model = self.tblHistory.model().sourceModel()
        model.addItems(data)
        if data:
            self.tblHistory.selectionModel().setCurrentIndex( model.index(0,0), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows )
        super(DiviPluginHistoryDialog, self).show()
    
    def historyChanged(self):
        """ Reaload data if window is visible """
        if self.isVisible():
            self.plugin.showHistoryDialog()
    
    def currentHistoryChanged(self, current, previous):
        self.new_geometry.reset()
        self.old_geometry.reset()
        self.tblChanges.model().removeRows()
        if not current.isValid():
            return
        item = current.data(Qt.UserRole)
        if item is None:
            data = {}
        else:
            data = current.data(Qt.UserRole).getDetails()
        with SetLocale_CtxDec():
            extent = None
            if data.get('new_geometry'):
                wkt = CreateGeometryFromJson( json.dumps(data['new_geometry']) ).ExportToWkt()
                geom = QgsGeometry.fromWkt( wkt )
                l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory')
                self.new_geometry.setToGeometry( geom, l )
                extent = QgsRectangle(geom.boundingBox())
            if data.get('old_geometry'):
                wkt = CreateGeometryFromJson( json.dumps(data['old_geometry']) ).ExportToWkt()
                geom = QgsGeometry.fromWkt( wkt )
                l = QgsVectorLayer('Point?crs=epsg:4326', 'asd', 'memory')
                self.old_geometry.setToGeometry( geom, l )
                if extent is None:
                    extent = QgsRectangle(geom.boundingBox())
                else:
                    extent.combineExtentWith( geom.boundingBox() )
            if extent is not None:
                extent.grow(0.01)
                self.mapCanvas.setExtent( extent )
                self.mapCanvas.refresh()
        if data.get('what_attributes', []):
            self.tblChanges.model().insertRows( 0, data.get('what_attributes', []) )
class TrackWidget(QWidget, Ui_Track):

    def __init__(self, parent=None):
        super(TrackWidget, self).__init__(parent)
        self.setupUi(self)
        self.title = "Display track"
        self.canvas = None
        self.band = None
        self.color_btn = None
        self.center = False
        self.position = None
        self.geom_type = None
        self.marker = None
        self.hidden = False
        self.centerButton.setEnabled(False)
        icon = QIcon(":/resources/mActionSave.svg")
        self.save_track_pushButton.setIcon(icon)
        self.save_track_pushButton.setToolTip("Save Track")
        self.save_track_pushButton.clicked.connect(self.save_track)

    def init(self, title, canvas, default_color, geom_type, marker):
        self.canvas = canvas
        self.geom_type = geom_type
        self.track_groupBox.setTitle(title)
        if marker:
            # Add marker
            self.marker = marker
            self.with_marker = True
        else:
            self.with_marker = False
        # Add rubber band
        self.band = QgsRubberBand(self.canvas, self.geom_type)
        if self.geom_type == QgsWkbTypes.PointGeometry:
            self.band.setIcon(QgsRubberBand.ICON_CIRCLE)
            self.band.setIconSize(12)
        else:
            self.band.setWidth(3)
        # Add color button widget for picking color
        self.color_btn = QgsColorButton()
        self.horizontal_layout_color.insertWidget(1, self.color_btn, 0, Qt.AlignLeft)
        self.color_btn.colorChanged.connect(self.color_changed)
        self.color_btn.setDefaultColor(default_color)
        self.color_btn.setColor(default_color)
        # Set signals
        self.centerButton.clicked.connect(self.center_to_location)
        self.clearTrackButton.clicked.connect(self.clear_track)

    def color_changed(self):
        transparent_color = QColor(self.color_btn.color())
        transparent_color.setAlpha(80)
        self.band.setColor(transparent_color)
        if self.with_marker:
            self.marker.set_color(QColor(self.color_btn.color()))

    def add_position(self, position):
        self.band.addPoint(position)

    def track_update_canvas(self, position, heading):
        self.centerButton.setEnabled(True)
        self.position = position
        self.add_position(position)
        if self.with_marker:
            self.marker.set_center(position, heading)
        if self.isHidden():
            if self.with_marker:
                self.marker.hide()
            self.band.hide()
        else:
            if self.with_marker:
                self.marker.show()
            self.band.show()

    def center_to_location(self):
        """
        Center to last received position on the map.
        """
        rect = QgsRectangle(self.position, self.position)
        self.canvas.setExtent(rect)
        self.canvas.zoomScale(400)
        self.canvas.refresh()

    def hide_band(self):
        self.band.hide()

    def hide_marker(self):
        if self.with_marker:
            self.marker.hide()

    def clear_track(self):
        self.band.reset(self.geom_type)

    def save_track(self):
        """
        Save the track to disk
        """
        layer_name, selected_filter = QFileDialog.getSaveFileName(None, 'Save Track', "",
                                                                  'Shapefile (*.shp);;KML (*.kml);;GPX (*.gpx)')

        if layer_name != '':

            if self.geom_type == QgsWkbTypes.PointGeometry:
                geometric_object = "MultiPoint?crs=epsg:4326"
            else:
                geometric_object = "LineString?crs=epsg:4326"

            layer = QgsVectorLayer(
                geometric_object,
                layer_name,
                "memory")
            feature = QgsFeature()
            feature.setGeometry(self.band.asGeometry())
            layer.dataProvider().addFeatures([feature])

            if selected_filter == "Shapefile (*.shp)":

                if not layer_name.endswith('.shp'):
                    layer_name = layer_name + '.shp'
                ret = QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8",
                                                              QgsCoordinateReferenceSystem(4326,
                                                                                           QgsCoordinateReferenceSystem.EpsgCrsId),
                                                              "ESRI Shapefile")
                if ret == QgsVectorFileWriter.NoError:
                    logger.info(layer.name() + " saved to " + layer_name)

            elif selected_filter == "KML (*.kml)":

                if not layer_name.endswith('.kml'):
                    layer_name = layer_name + '.kml'

                QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8",
                                                        QgsCoordinateReferenceSystem(4326,
                                                                                     QgsCoordinateReferenceSystem.EpsgCrsId),
                                                        "KML")
            elif selected_filter == "GPX (*.gpx)":

                if not layer_name.endswith('.gpx'):
                    layer_name = layer_name + '.gpx'
                ds_options = list()
                ds_options.append("GPX_USE_EXTENSIONS=TRUE")
                QgsVectorFileWriter.writeAsVectorFormat(layer, layer_name, "utf-8",
                                                        QgsCoordinateReferenceSystem(4326,
                                                                                     QgsCoordinateReferenceSystem.EpsgCrsId),
                                                        "GPX",
                                                        datasourceOptions=ds_options)

    def close(self):
        self.hide_band()
        self.hide_marker()
Example #9
0
class FinderBox(QComboBox):

    running = False
    toFinish = 0

    searchStarted = pyqtSignal()
    searchFinished = pyqtSignal()

    def __init__(self, finders, iface, parent=None):
        self.iface = iface
        self.mapCanvas = iface.mapCanvas()
        self.rubber = QgsRubberBand(self.mapCanvas)
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

        QComboBox.__init__(self, parent)
        self.setEditable(True)
        self.setInsertPolicy(QComboBox.InsertAtTop)
        self.setMinimumHeight(27)
        self.setSizePolicy(QSizePolicy.Expanding,
                           QSizePolicy.Fixed)

        self.insertSeparator(0)
        self.lineEdit().returnPressed.connect(self.search)

        self.resultView = QTreeView()
        self.resultView.setHeaderHidden(True)
        self.resultView.setMinimumHeight(300)
        self.resultView.activated.connect(self.itemActivated)
        self.resultView.pressed.connect(self.itemPressed)
        self.setView(self.resultView)

        self.resultModel = ResultModel(self)
        self.setModel(self.resultModel)

        self.finders = finders
        for finder in self.finders.values():
            finder.resultFound.connect(self.resultFound)
            finder.limitReached.connect(self.limitReached)
            finder.finished.connect(self.finished)

        self.clearButton = QPushButton(self)
        self.clearButton.setIcon(QIcon(":/plugins/quickfinder/icons/draft.svg"))
        self.clearButton.setText('')
        self.clearButton.setFlat(True)
        self.clearButton.setCursor(QCursor(Qt.ArrowCursor))
        self.clearButton.setStyleSheet('border: 0px; padding: 0px;')
        self.clearButton.clicked.connect(self.clear)

        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.addStretch()
        layout.addWidget(self.clearButton)
        layout.addSpacing(20)

        buttonSize = self.clearButton.sizeHint()
        # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
        padding = buttonSize.width()  # + frameWidth + 1
        self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding)

    def __del__(self):
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    def clearSelection(self):
        self.resultModel.setSelected(None, self.resultView.palette())
        self.rubber.reset()

    def clear(self):
        self.clearSelection()
        self.resultModel.clearResults()
        self.lineEdit().setText('')

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.clearSelection()
        QComboBox.keyPressEvent(self, event)

    def search(self):
        if self.running:
            return

        toFind = self.lineEdit().text()
        if not toFind or toFind == '':
            return

        self.running = True
        self.searchStarted.emit()

        self.clearSelection()
        self.resultModel.clearResults()
        self.resultModel.truncateHistory(MySettings().value("historyLength"))
        self.resultModel.setLoading(True)
        self.showPopup()

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        self.findersToStart = []
        for finder in self.finders.values():
            if finder.activated():
                self.findersToStart.append(finder)

        bbox = self.mapCanvas.fullExtent()

        while len(self.findersToStart) > 0:
            finder = self.findersToStart[0]
            self.findersToStart.remove(finder)
            self.resultModel.addResult(finder.name)
            finder.start(toFind, bbox=bbox)

        # For case there is no finder activated
        self.finished(None)

    def stop(self):
        self.findersToStart = []
        for finder in self.finders.values():
            if finder.isRunning():
                finder.stop()
        self.finished(None)

    def resultFound(self, finder, layername, value, geometry, srid):
        self.resultModel.addResult(finder.name, layername, value, geometry, srid)
        self.resultView.expandAll()

    def limitReached(self, finder, layername):
        self.resultModel.addEllipsys(finder.name, layername)

    def finished(self, finder):
        if len(self.findersToStart) > 0:
            return
        for finder in self.finders.values():
            if finder.isRunning():
                return

        self.running = False
        self.searchFinished.emit()

        self.resultModel.setLoading(False)

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

    def itemActivated(self, index):
        item = self.resultModel.itemFromIndex(index)
        self.showItem(item)

    def itemPressed(self, index):
        item = self.resultModel.itemFromIndex(index)
        if QApplication.mouseButtons() == Qt.LeftButton:
            self.showItem(item)

    def showItem(self, item):
        if isinstance(item, ResultItem):
            self.resultModel.setSelected(item, self.resultView.palette())
            geometry = self.transformGeom(item)
            self.rubber.reset(geometry.type())
            self.rubber.setToGeometry(geometry, None)
            self.zoomToRubberBand()
            return

        if isinstance(item, GroupItem):
            child = item.child(0)
            if isinstance(child, ResultItem):
                self.resultModel.setSelected(item, self.resultView.palette())
                self.rubber.reset(child.geometry.type())
                for i in xrange(0, item.rowCount()):
                    geometry = self.transformGeom(item.child(i))
                    self.rubber.addGeometry(geometry, None)
                self.zoomToRubberBand()
            return

        if item.__class__.__name__ == 'QStandardItem':
            self.clearSelection()

    def transformGeom(self, item):
        src_crs = QgsCoordinateReferenceSystem()
        src_crs.createFromSrid(item.srid)
        dest_crs = self.mapCanvas.mapRenderer().destinationCrs()
        geom = QgsGeometry(item.geometry)
        geom.transform(QgsCoordinateTransform(src_crs, dest_crs))
        return geom

    def zoomToRubberBand(self):
        geom = self.rubber.asGeometry()
        if geom:
            rect = geom.boundingBox()
            rect.scale(1.5)
            self.mapCanvas.setExtent(rect)
            self.mapCanvas.refresh()
class SwissLocatorFilter(QgsLocatorFilter):

    HEADERS = {
        b'User-Agent': b'Mozilla/5.0 QGIS Swiss Geoportal Locator Filter'
    }

    message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget)

    def __init__(self,
                 filter_type: FilterType,
                 iface: QgisInterface = None,
                 crs: str = None):
        """"
        :param filter_type: the type of filter
        :param locale_lang: the language of the locale.
        :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise
        :param crs: if iface is not given, it shall be provided, see clone()
        """
        super().__init__()
        self.type = filter_type
        self.rubber_band = None
        self.feature_rubber_band = None
        self.iface = iface
        self.map_canvas = None
        self.settings = Settings()
        self.transform_ch = None
        self.transform_4326 = None
        self.map_tip = None
        self.current_timer = None
        self.crs = None
        self.event_loop = None
        self.result_found = False
        self.access_managers = {}
        self.nam_map_tip = None
        self.nam_fetch_feature = None

        if crs:
            self.crs = crs

        self.lang = get_language()

        self.searchable_layers = searchable_layers(self.lang, restrict=True)

        if iface is not None:
            # happens only in main thread
            self.map_canvas = iface.mapCanvas()
            self.map_canvas.destinationCrsChanged.connect(
                self.create_transforms)

            self.rubber_band = QgsRubberBand(self.map_canvas,
                                             QgsWkbTypes.PointGeometry)
            self.rubber_band.setColor(QColor(255, 255, 50, 200))
            self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE)
            self.rubber_band.setIconSize(15)
            self.rubber_band.setWidth(4)
            self.rubber_band.setBrushStyle(Qt.NoBrush)

            self.feature_rubber_band = QgsRubberBand(
                self.map_canvas, QgsWkbTypes.PolygonGeometry)
            self.feature_rubber_band.setColor(QColor(255, 50, 50, 200))
            self.feature_rubber_band.setFillColor(QColor(255, 255, 50, 160))
            self.feature_rubber_band.setBrushStyle(Qt.SolidPattern)
            self.feature_rubber_band.setLineStyle(Qt.SolidLine)
            self.feature_rubber_band.setWidth(4)

            self.create_transforms()

    def name(self):
        return '{}_{}'.format(self.__class__.__name__,
                              FilterType(self.type).name)

    def clone(self):
        return SwissLocatorFilter(self.type, crs=self.crs)

    def priority(self):
        return self.settings.value(
            '{type}_priority'.format(type=self.type.value))

    def displayName(self):
        if self.type is FilterType.Location:
            return self.tr('Swiss Geoportal locations')
        elif self.type is FilterType.WMS:
            return self.tr('Swiss Geoportal / opendata.swiss WMS layers')
        elif self.type is FilterType.Feature:
            return self.tr('Swiss Geoportal features')
        else:
            raise NameError('Filter type is not valid.')

    def prefix(self):
        if self.type is FilterType.Location:
            return 'chl'
        elif self.type is FilterType.WMS:
            return 'chw'
        elif self.type is FilterType.Feature:
            return 'chf'
        else:
            raise NameError('Filter type is not valid.')

    def clearPreviousResults(self):
        self.rubber_band.reset(QgsWkbTypes.PointGeometry)
        self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry)
        if self.map_tip is not None:
            del self.map_tip
            self.map_tip = None
        if self.current_timer is not None:
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def hasConfigWidget(self):
        return True

    def openConfigWidget(self, parent=None):
        dlg = ConfigDialog(parent)
        wid = dlg.findChild(QTabWidget, "tabWidget", Qt.FindDirectChildrenOnly)
        tab = wid.findChild(QWidget, self.type.value)
        wid.setCurrentWidget(tab)
        dlg.exec_()

    def create_transforms(self):
        # this should happen in the main thread
        self.crs = self.settings.value('crs')
        if self.crs == 'project':
            map_crs = self.map_canvas.mapSettings().destinationCrs()
            if map_crs.isValid():
                self.crs = map_crs.authid().split(':')[1]
            if self.crs not in AVAILABLE_CRS:
                self.crs = '2056'
        assert self.crs in AVAILABLE_CRS
        src_crs_ch = QgsCoordinateReferenceSystem('EPSG:{}'.format(self.crs))
        assert src_crs_ch.isValid()
        dst_crs = self.map_canvas.mapSettings().destinationCrs()
        self.transform_ch = QgsCoordinateTransform(src_crs_ch, dst_crs,
                                                   QgsProject.instance())

        src_crs_4326 = QgsCoordinateReferenceSystem('EPSG:4326')
        self.transform_4326 = QgsCoordinateTransform(src_crs_4326, dst_crs,
                                                     QgsProject.instance())

    def group_info(self, group: str) -> (str, str):
        groups = {
            'zipcode': {
                'name': self.tr('ZIP code'),
                'layer': 'ch.swisstopo-vd.ortschaftenverzeichnis_plz'
            },
            'gg25': {
                'name': self.tr('Municipal boundaries'),
                'layer': 'ch.swisstopo.swissboundaries3d-gemeinde-flaeche.fill'
            },
            'district': {
                'name': self.tr('District'),
                'layer': 'ch.swisstopo.swissboundaries3d-bezirk-flaeche.fill'
            },
            'kantone': {
                'name': self.tr('Cantons'),
                'layer': 'ch.swisstopo.swissboundaries3d-kanton-flaeche.fill'
            },
            'gazetteer': {
                'name': self.tr('Index'),
                'layer': 'ch.swisstopo.swissnames3d'
            },  # there is also: ch.bav.haltestellen-oev ?
            'address': {
                'name': self.tr('Address'),
                'layer': 'ch.bfs.gebaeude_wohnungs_register'
            },
            'parcel': {
                'name': self.tr('Parcel'),
                'layer': None
            }
        }
        if group not in groups:
            self.info('Could not find group {} in dictionary'.format(group))
            return None, None
        return groups[group]['name'], groups[group]['layer']

    @staticmethod
    def rank2priority(rank) -> float:
        """
        Translate the rank from geoportal to the priority of the result
        see https://api3.geo.admin.ch/services/sdiservices.html#search
        :param rank: an integer from 1 to 7
        :return: the priority as a float from 0 to 1, 1 being a perfect match
        """
        return float(-rank / 7 + 1)

    @staticmethod
    def box2geometry(box: str) -> QgsRectangle:
        """
        Creates a rectangle from a Box definition as string
        :param box: the box as a string
        :return: the rectangle
        """
        coords = re.findall(r'\b(\d+(?:\.\d+)?)\b', box)
        if len(coords) != 4:
            raise InvalidBox('Could not parse: {}'.format(box))
        return QgsRectangle(float(coords[0]), float(coords[1]),
                            float(coords[2]), float(coords[3]))

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url.url()

    def fetchResults(self, search: str, context: QgsLocatorContext,
                     feedback: QgsFeedback):
        try:
            self.dbg_info("start Swiss locator search...")

            if len(search) < 2:
                return

            if len(search) < 4 and self.type is FilterType.Feature:
                return

            self.result_found = False

            swisstopo_base_url = 'https://api3.geo.admin.ch/rest/services/api/SearchServer'
            swisstopo_base_params = {
                'type':
                self.type.value,
                'searchText':
                str(search),
                'returnGeometry':
                'true',
                'lang':
                self.lang,
                'sr':
                self.crs,
                'limit':
                str(
                    self.settings.value(
                        '{type}_limit'.format(type=self.type.value)))
                # bbox Must be provided if the searchText is not.
                # A comma separated list of 4 coordinates representing
                # the bounding box on which features should be filtered (SRID: 21781).
            }
            # Locations, WMS layers
            if self.type is not FilterType.Feature:
                nam = NetworkAccessManager()
                feedback.canceled.connect(nam.abort)

                search_urls = [(swisstopo_base_url, swisstopo_base_params)]

                if self.settings.value('layers_include_opendataswiss'
                                       ) and self.type is FilterType.WMS:
                    search_urls.append(
                        ('https://opendata.swiss/api/3/action/package_search?',
                         {
                             'q': 'q=WMS+%C3' + str(search)
                         }))

                for (swisstopo_base_url, swisstopo_base_params) in search_urls:
                    swisstopo_base_url = self.url_with_param(
                        swisstopo_base_url, swisstopo_base_params)
                    self.dbg_info(swisstopo_base_url)
                    try:
                        (response, content) = nam.request(swisstopo_base_url,
                                                          headers=self.HEADERS,
                                                          blocking=True)
                        self.handle_response(response, search, feedback)
                    except RequestsExceptionUserAbort:
                        pass
                    except RequestsException as err:
                        self.info(err)

            # Feature search
            else:
                # Feature search is split in several requests
                # otherwise URL is too long
                self.access_managers = {}
                try:
                    layers = list(self.searchable_layers.keys())
                    assert len(layers) > 0
                    step = 30
                    for l in range(0, len(layers), step):
                        last = min(l + step - 1, len(layers) - 1)
                        swisstopo_base_params['features'] = ','.join(
                            layers[l:last])
                        self.access_managers[self.url_with_param(
                            swisstopo_base_url, swisstopo_base_params)] = None
                except IOError:
                    self.info(
                        'Layers data file not found. Please report an issue.',
                        Qgis.Critical)

                # init event loop
                # wait for all requests to end
                self.event_loop = QEventLoop()

                def reply_finished(response):
                    self.handle_response(response, search, feedback)
                    if response.url in self.access_managers:
                        self.access_managers[response.url] = None
                    for nam in self.access_managers.values():
                        if nam is not None:
                            return
                        self.event_loop.quit()

                feedback.canceled.connect(self.event_loop.quit)

                # init the network access managers, create the URL
                for swisstopo_base_url in self.access_managers:
                    self.dbg_info(swisstopo_base_url)
                    nam = NetworkAccessManager()
                    self.access_managers[swisstopo_base_url] = nam
                    nam.finished.connect(reply_finished)
                    nam.request(swisstopo_base_url,
                                headers=self.HEADERS,
                                blocking=False)
                    feedback.canceled.connect(nam.abort)

                # Let the requests end and catch all exceptions (and clean up requests)
                if len(self.access_managers) > 0:
                    try:
                        self.event_loop.exec_(
                            QEventLoop.ExcludeUserInputEvents)
                    except RequestsExceptionUserAbort:
                        pass
                    except RequestsException as err:
                        self.info(str(err))

            if not self.result_found:
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = self.tr('No result found.')
                result.userData = NoResult().as_definition()
                self.resultFetched.emit(result)

        except Exception as e:
            self.info(e, Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def handle_response(self, response, search: str, feedback: QgsFeedback):
        try:
            if response.status_code != 200:
                if not isinstance(response.exception,
                                  RequestsExceptionUserAbort):
                    self.info(
                        "Error in main response with status code: {} from {}".
                        format(response.status_code, response.url))
                return

            data = json.loads(response.content.decode('utf-8'))
            # self.dbg_info(data)

            if self.is_opendata_swiss_response(data):
                visited_capabilities = []

                for loc in data['result']['results']:
                    display_name = loc['title'].get(self.lang, "")
                    if not display_name:
                        # Fallback to german
                        display_name = loc['title']['de']

                    for res in loc['resources']:

                        url = res['url']
                        url_components = urlparse(url)
                        wms_url = url_components.scheme + '://' + url_components.netloc + '/' + url_components.path + '?'

                        result = QgsLocatorResult()
                        result.filter = self
                        result.group = 'opendata.swiss'
                        result.icon = QgsApplication.getThemeIcon(
                            "/mActionAddWmsLayer.svg")

                        if 'wms' in url.lower():
                            if res['media_type'] == 'WMS':
                                result.displayString = display_name
                                result.description = url

                                if res['title']['de'] == 'GetMap':
                                    layers = parse_qs(
                                        url_components.query)['LAYERS']
                                    result.userData = WMSLayerResult(
                                        layer=layers[0],
                                        title=display_name,
                                        url=wms_url).as_definition()
                                    self.result_found = True
                                    self.resultFetched.emit(result)

                            elif 'request=getcapabilities' in url.lower(
                            ) and url_components.netloc not in visited_capabilities:
                                visited_capabilities.append(
                                    url_components.netloc)

                                def parse_capabilities_result(response):
                                    capabilities = ET.fromstring(
                                        response.content)

                                    # Get xml namespace
                                    match = re.match(r'\{.*\}',
                                                     capabilities.tag)
                                    namespace = match.group(0) if match else ''

                                    # Search for layers containing the search term in the name or title
                                    for layer in capabilities.findall(
                                            './/{}Layer'.format(namespace)):
                                        layername = self.find_text(
                                            layer, '{}Name'.format(namespace))
                                        layertitle = self.find_text(
                                            layer, '{}Title'.format(namespace))
                                        if layername and (
                                                search in layername.lower() or
                                                search in layertitle.lower()):
                                            if not layertitle:
                                                layertitle = layername

                                            result.displayString = layertitle
                                            result.description = '{}?LAYERS={}'.format(
                                                url.replace(
                                                    'GetCapabilities',
                                                    'GetMap'), layername)
                                            result.userData = WMSLayerResult(
                                                layer=layername,
                                                title=layertitle,
                                                url=wms_url).as_definition()
                                            self.result_found = True
                                            self.resultFetched.emit(result)

                                    self.event_loop.quit()

                                # Retrieve Capabilities xml
                                self.event_loop = QEventLoop()
                                nam = NetworkAccessManager()
                                nam.finished.connect(parse_capabilities_result)
                                nam.request(url,
                                            headers=self.HEADERS,
                                            blocking=False)
                                feedback.canceled.connect(self.event_loop.quit)

                                try:
                                    self.event_loop.exec_(
                                        QEventLoop.ExcludeUserInputEvents)
                                except RequestsExceptionUserAbort:
                                    pass
                                except RequestsException as err:
                                    self.info(err)

            else:
                for loc in data['results']:
                    self.dbg_info("keys: {}".format(loc['attrs'].keys()))

                    result = QgsLocatorResult()
                    result.filter = self
                    result.group = 'Swiss Geoportal'
                    if loc['attrs']['origin'] == 'layer':
                        # available keys: ['origin', 'lang', 'layer', 'staging', 'title', 'topics', 'detail', 'label', 'id']
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        result.displayString = loc['attrs']['title']
                        result.description = loc['attrs']['layer']
                        result.userData = WMSLayerResult(
                            layer=loc['attrs']['layer'],
                            title=loc['attrs']['title'],
                            url='http://wms.geo.admin.ch/?VERSION%3D2.0.0'
                        ).as_definition()
                        result.icon = QgsApplication.getThemeIcon(
                            "/mActionAddWmsLayer.svg")
                        self.result_found = True
                        self.resultFetched.emit(result)

                    elif loc['attrs']['origin'] == 'feature':
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        layer = loc['attrs']['layer']
                        point = QgsPointXY(loc['attrs']['lon'],
                                           loc['attrs']['lat'])
                        if layer in self.searchable_layers:
                            layer_display = self.searchable_layers[layer]
                        else:
                            self.info(
                                self.
                                tr('Layer {} is not in the list of searchable layers.'
                                   ' Please report issue.'.format(layer)),
                                Qgis.Warning)
                            layer_display = layer
                        result.group = layer_display
                        result.displayString = loc['attrs']['detail']
                        result.userData = FeatureResult(
                            point=point,
                            layer=layer,
                            feature_id=loc['attrs']
                            ['feature_id']).as_definition()
                        result.icon = QIcon(
                            ":/plugins/swiss_locator/icons/swiss_locator.png")
                        self.result_found = True
                        self.resultFetched.emit(result)

                    else:  # locations
                        for key, val in loc['attrs'].items():
                            self.dbg_info('{}: {}'.format(key, val))
                        group_name, group_layer = self.group_info(
                            loc['attrs']['origin'])
                        if 'layerBodId' in loc['attrs']:
                            self.dbg_info("layer: {}".format(
                                loc['attrs']['layerBodId']))
                        if 'featureId' in loc['attrs']:
                            self.dbg_info("feature: {}".format(
                                loc['attrs']['featureId']))

                        result.displayString = strip_tags(
                            loc['attrs']['label'])
                        # result.description = loc['attrs']['detail']
                        # if 'featureId' in loc['attrs']:
                        #     result.description = loc['attrs']['featureId']
                        result.group = group_name
                        result.userData = LocationResult(
                            point=QgsPointXY(loc['attrs']['y'],
                                             loc['attrs']['x']),
                            bbox=self.box2geometry(
                                loc['attrs']['geom_st_box2d']),
                            layer=group_layer,
                            feature_id=loc['attrs']['featureId']
                            if 'featureId' in loc['attrs'] else None,
                            html_label=loc['attrs']['label']).as_definition()
                        result.icon = QIcon(
                            ":/plugins/swiss_locator/icons/swiss_locator.png")
                        self.result_found = True
                        self.resultFetched.emit(result)

        except Exception as e:
            self.info(str(e), Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def triggerResult(self, result: QgsLocatorResult):
        # this should be run in the main thread, i.e. mapCanvas should not be None

        # remove any map tip
        self.clearPreviousResults()

        user_data = NoResult
        try:
            swiss_result = result_from_data(result)
        except SystemError:
            self.message_emitted.emit(
                self.displayName(),
                self.
                tr('QGIS Swiss Locator encountered an error. Please <b>update to QGIS 3.16.2</b> or newer.'
                   ), Qgis.Warning, None)

        if type(swiss_result) == NoResult:
            return

        # WMS
        if type(swiss_result) == WMSLayerResult:
            url_with_params = 'contextualWMSLegend=0' \
                              '&crs=EPSG:{crs}' \
                              '&dpiMode=7' \
                              '&featureCount=10' \
                              '&format=image/png' \
                              '&layers={layer}' \
                              '&styles=' \
                              '&url={url}' \
                .format(crs=self.crs, layer=swiss_result.layer, url=swiss_result.url)
            wms_layer = QgsRasterLayer(url_with_params, result.displayString,
                                       'wms')
            label = QLabel()
            label.setTextFormat(Qt.RichText)
            label.setTextInteractionFlags(Qt.TextBrowserInteraction)
            label.setOpenExternalLinks(True)

            if 'geo.admin.ch' in swiss_result.url.lower():
                label.setText(
                    '<a href="https://map.geo.admin.ch/'
                    '?lang={}&bgLayer=ch.swisstopo.pixelkarte-farbe&layers={}">'
                    'Open layer in map.geo.admin.ch</a>'.format(
                        self.lang, swiss_result.layer))

            if not wms_layer.isValid():
                msg = self.tr('Cannot load WMS layer: {} ({})'.format(
                    swiss_result.title, swiss_result.layer))
                level = Qgis.Warning
                self.info(msg, level)
            else:
                msg = self.tr('WMS layer added to the map: {} ({})'.format(
                    swiss_result.title, swiss_result.layer))
                level = Qgis.Info

                QgsProject.instance().addMapLayer(wms_layer)

            self.message_emitted.emit(self.displayName(), msg, level, label)

        # Feature
        elif type(swiss_result) == FeatureResult:
            point = QgsGeometry.fromPointXY(swiss_result.point)
            point.transform(self.transform_4326)
            self.highlight(point)
            if self.settings.value('show_map_tip'):
                self.show_map_tip(swiss_result.layer, swiss_result.feature_id,
                                  point)
        # Location
        else:
            point = QgsGeometry.fromPointXY(swiss_result.point)
            if swiss_result.bbox.isNull():
                bbox = None
            else:
                bbox = QgsGeometry.fromRect(swiss_result.bbox)
                bbox.transform(self.transform_ch)
            layer = swiss_result.layer
            feature_id = swiss_result.feature_id
            if not point:
                return

            point.transform(self.transform_ch)

            self.highlight(point, bbox)

            if layer and feature_id:
                self.fetch_feature(layer, feature_id)

                if self.settings.value('show_map_tip'):
                    self.show_map_tip(layer, feature_id, point)
            else:
                self.current_timer = QTimer()
                self.current_timer.timeout.connect(self.clearPreviousResults)
                self.current_timer.setSingleShot(True)
                self.current_timer.start(5000)

    def highlight(self, point, bbox=None):
        if bbox is None:
            bbox = point
        self.rubber_band.reset(QgsWkbTypes.PointGeometry)
        self.rubber_band.addGeometry(point, None)
        rect = bbox.boundingBox()
        rect.scale(1.1)
        self.map_canvas.setExtent(rect)
        self.map_canvas.refresh()

    def fetch_feature(self, layer, feature_id):
        # Try to get more info
        self.nam_fetch_feature = NetworkAccessManager()
        url_detail = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}' \
            .format(layer=layer, feature_id=feature_id)
        params = {'lang': self.lang, 'sr': self.crs}
        url_detail = self.url_with_param(url_detail, params)
        self.dbg_info(url_detail)
        self.nam_fetch_feature.finished.connect(self.parse_feature_response)
        self.nam_fetch_feature.request(url_detail,
                                       headers=self.HEADERS,
                                       blocking=False)

    def parse_feature_response(self, response):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info(
                    "Error in feature response with status code: {} from {}".
                    format(response.status_code, response.url))
            return

        data = json.loads(response.content.decode('utf-8'))
        self.dbg_info(data)

        if 'feature' not in data or 'geometry' not in data['feature']:
            return

        if 'rings' in data['feature']['geometry']:
            rings = data['feature']['geometry']['rings']
            self.dbg_info(rings)
            for r in range(0, len(rings)):
                for p in range(0, len(rings[r])):
                    rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1])
            geometry = QgsGeometry.fromPolygonXY(rings)
            geometry.transform(self.transform_ch)

            self.feature_rubber_band.reset(QgsWkbTypes.PolygonGeometry)
            self.feature_rubber_band.addGeometry(geometry, None)

    def show_map_tip(self, layer, feature_id, point):
        if layer and feature_id:
            url_html = 'https://api3.geo.admin.ch/rest/services/api/MapServer/{layer}/{feature_id}/htmlPopup' \
                .format(layer=layer, feature_id=feature_id)
            params = {'lang': self.lang, 'sr': self.crs}
            url_html = self.url_with_param(url_html, params)
            self.dbg_info(url_html)

            self.nam_map_tip = NetworkAccessManager()
            self.nam_map_tip.finished.connect(
                lambda response: self.parse_map_tip_response(response, point))
            self.nam_map_tip.request(url_html,
                                     headers=self.HEADERS,
                                     blocking=False)

    def parse_map_tip_response(self, response, point):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info(
                    "Error in map tip response with status code: {} from {}".
                    format(response.status_code, response.url))
            return

        self.dbg_info(response.content.decode('utf-8'))
        self.map_tip = MapTip(self.iface, response.content.decode('utf-8'),
                              point.asPoint())
        self.map_tip.closed.connect(self.clearPreviousResults)

    def info(self, msg="", level=Qgis.Info):
        self.logMessage(str(msg), level)

    def dbg_info(self, msg=""):
        if DEBUG:
            self.info(msg)

    @staticmethod
    def break_camelcase(identifier):
        matches = re.finditer(
            '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)',
            identifier)
        return ' '.join([m.group(0) for m in matches])

    def is_opendata_swiss_response(self, json):
        return 'opendata.swiss' in json.get("help", [])

    def find_text(self, xmlElement, match):
        node = xmlElement.find(match)
        return node.text if node is not None else ''
Example #11
0
class InterpolateTool(QgsMapToolAdvancedDigitizing):
    """
    Map tool class to interpolate an elevation in the middle of a segment
    """

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(), iface.cadDockWidget())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/interpolate_icon.png'
        self.text = QCoreApplication.translate(
            "VDLTools", "Interpolate the elevation of a vertex and a point in the middle of a line")
        self.__layer = None
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__lastFeatureId = None
        self.__layerList = None
        self.__lastLayer = None
        self.__confDlg = None
        self.__mapPoint = None
        self.__rubber = None
        self.__selectedFeature = None
        self.__findVertex = False

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapToolAdvancedDigitizing.activate(self)
        self.__updateList()
        self.__rubber = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setWidth(2)
        self.__rubber.setIconSize(20)
        self.canvas().layersChanged.connect(self.__updateList)
        self.canvas().scaleChanged.connect(self.__updateList)
        self.setMode(self.CaptureLine)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__done()
        self.__cancel()
        self.__rubber = None
        Signal.safelyDisconnect(self.canvas().layersChanged, self.__updateList)
        Signal.safelyDisconnect(self.canvas().scaleChanged, self.__updateList)
        QgsMapToolAdvancedDigitizing.deactivate(self)

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()

    def __done(self):
        """
        When the edition is finished
        """
        self.__isEditing = False
        self.__confDlg = None
        self.__mapPoint = None

    def __cancel(self):
        """
        To cancel used variables
        """
        self.__findVertex = False
        if self.__lastLayer is not None:
            self.__lastLayer.removeSelection()
            self.__lastLayer = None
        if self.__rubber is not None:
           self.__rubber.reset()
        self.__lastFeatureId = None
        self.__selectedFeature = None

    def __removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
            else:
                Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type() == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point:
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
                else:
                    Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = layer
            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.canvas().mapTool() == self:
                    self.__iface.actionPan().trigger()
            return
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()
        self.action().setEnabled(False)
        self.__removeLayer()

    def __updateList(self):
        """
        To update the line layers list that we can use for interpolation
        """
        self.__layerList = []
        for layer in self.canvas().layers():
            if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType() \
                    and layer.geometryType() == QGis.Line:
                        self.__layerList.append(QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.All, 10,
                                                                             QgsTolerance.Pixels))

    def keyReleaseEvent(self, event):
        """
        When keyboard is pressed
        :param event: keyboard event
        """
        if event.key() == Qt.Key_Escape:
            self.__done()
            self.__cancel()

    def cadCanvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """

        if type(event) == QMoveEvent:
            map_point = self.toMapCoordinates(event.pos())
        else:
            map_point = event.mapPoint()

        if not self.__isEditing and not self.__findVertex and self.__layerList is not None:
            f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), self.__layerList)

            if f_l is not None and self.__lastFeatureId != f_l[0].id():
                f = f_l[0]
                self.__lastFeatureId = f.id()
                if self.__lastLayer is not None:
                    self.__lastLayer.removeSelection()
                self.__lastLayer = f_l[1]
                self.__lastLayer.setSelectedFeatures([f.id()])
            if f_l is None and self.__lastLayer is not None:
                self.__lastLayer.removeSelection()
                self.__lastFeatureId = None
        elif self.__findVertex:
            self.__rubber.reset()
            snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All)
            match = Finder.snap(map_point, self.canvas(), snap_layers)
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        self.__rubber.setIcon(4)
                        self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None)
                    else:
                        intersection = Finder.snapCurvedIntersections(point, self.canvas(), self,
                                                                      self.__selectedFeature.id())
                        if intersection is not None:
                            if self.__isVertexUnderPoint(intersection, snap_layers):
                                self.__rubber.setIcon(4)
                            else:
                                self.__rubber.setIcon(1)
                            self.__rubber.setToGeometry(QgsGeometry().fromPoint(intersection), None)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(point, self.canvas(), self,
                                                                  self.__selectedFeature.id())
                    if intersection is not None:
                        if self.__isVertexUnderPoint(intersection, snap_layers):
                            self.__rubber.setIcon(4)
                        else:
                            self.__rubber.setIcon(1)
                        self.__rubber.setToGeometry(QgsGeometry().fromPoint(intersection), None)
                    elif self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        self.__rubber.setIcon(3)
                        self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None)

    def __isVertexUnderPoint(self, point, snap_layers):
        """
        When snapping find a point instead of line/polygon element, we need to check if there is a vertex under it
        :param point: coordinates
        :param snap_layers: layers configs
        :return: True if there is a vertex, False otherwise
        """
        for config in snap_layers:
            if config.layer.id() == self.__lastLayer.id():
                tolerance = config.tolerance
                if config.unit == QgsTolerance.Pixels:
                    tolerance = Finder.calcCanvasTolerance(self.toCanvasCoordinates(point), config.layer, self, tolerance)
                elif config.unit == QgsTolerance.ProjectUnits:
                    tolerance = Finder.calcMapTolerance(point, config.layer, self, tolerance)
                layPoint = self.toLayerCoordinates(config.layer, point)
                geom = self.__selectedFeature.geometry()
                dist = geom.closestVertex(layPoint)[4]
                if dist < (tolerance*tolerance):
                    return True
                break
        return False

    def cadCanvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        if self.__lastLayer is not None and not self.__findVertex:
            found_features = self.__lastLayer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "One feature at a time"),
                                                          level=QgsMessageBar.INFO)
                    return
                self.__selectedFeature = found_features[0]

                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools",
                                               "Select the position for interpolation (ESC to undo)"),
                    level=QgsMessageBar.INFO, duration=3)
                self.setMode(self.CaptureNone)
                self.__findVertex = True
        elif self.__findVertex:
            self.__rubber.reset()
            snap_layers = Finder.getLayersSettings(self.canvas(), [QGis.Line, QGis.Polygon], QgsPointLocator.All)
            match = Finder.snap(event.mapPoint(), self.canvas(), snap_layers)
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                ok = False
                noVertex = False
                if match.hasVertex():
                    if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():

                        ok = True
                        noVertex = True
                    else:
                        intersection = Finder.snapCurvedIntersections(point, self.canvas(), self,
                                                                      self.__selectedFeature.id())
                        if intersection is not None:
                            point = intersection
                            ok = True
                            if self.__isVertexUnderPoint(intersection, snap_layers):
                                noVertex = True

                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(point, self.canvas(), self,
                                                                  self.__selectedFeature.id())
                    if intersection is not None:
                        point = intersection
                        ok = True
                        if self.__isVertexUnderPoint(intersection, snap_layers):
                            noVertex = True
                    elif self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        ok = True
                if ok:
                    self.__isEditing = True
                    self.__findVertex = False
                    self.__mapPoint = point
                    if noVertex:
                        self.__ok(False, True)
                    else:
                        self.__confDlg = InterpolateConfirmDialog()
                        if self.__lastLayer.isEditable():
                            self.__confDlg.setMainLabel(QCoreApplication.translate("VDLTools", "What do you want to do ?"))
                            self.__confDlg.setAllLabel(QCoreApplication.translate("VDLTools", "Create point and new vertex"))
                            self.__confDlg.setVtLabel(QCoreApplication.translate("VDLTools", "Create only the vertex"))
                        self.__confDlg.rejected.connect(self.__done)
                        self.__confDlg.okButton().clicked.connect(self.__onConfirmOk)
                        self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel)
                        self.__confDlg.show()
            else:
                self.__done()
                self.__cancel()

    def __onConfirmCancel(self):
        """
        When the Cancel button in Interpolate Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __onConfirmOk(self):
        """
        When the Ok button in Interpolate Confirm Dialog is pushed
        """
        checkedId = self.__confDlg.getCheckedId()
        self.__confDlg.accept()

        withVertex = True
        withPoint = True
        if checkedId == 1:
            withVertex = False
        else:
            if not self.__lastLayer.isEditable():
                self.__lastLayer.startEditing()
        if checkedId == 2:
            withPoint = False

        self.__ok(withVertex, withPoint)

    def __ok(self, withVertex, withPoint):
        """
        To apply the interpolation
        :param withVertex: if we want a new interpolated vertex
        :param withPoint: if we want a new interpolated point
        """
        line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface)
        vertex_v2 = QgsPointV2()
        vertex_id = QgsVertexId()
        line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2, vertex_id, 0)

        x0 = line_v2.xAt(vertex_id.vertex-1)
        y0 = line_v2.yAt(vertex_id.vertex-1)
        d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y())
        x1 = line_v2.xAt(vertex_id.vertex)
        y1 = line_v2.yAt(vertex_id.vertex)
        d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y())
        z0 = line_v2.zAt(vertex_id.vertex-1)
        z1 = line_v2.zAt(vertex_id.vertex)
        z = old_div((d0*z1 + d1*z0), (d0 + d1))
        vertex_v2.addZValue(round(z, 2))

        if withPoint:
            pt_feat = QgsFeature(self.__layer.pendingFields())
            pt_feat.setGeometry(QgsGeometry(vertex_v2))
            for i in range(len(self.__layer.pendingFields())):
                # default = self.__layer.defaultValue(i, pt_feat)
                # if default is not None:
                #     print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default)
                #     print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i))

                e = QgsExpression(self.__layer.defaultValueExpression(i))
                default = e.evaluate(pt_feat)
                pt_feat.setAttribute(i, default)

            if self.__layer.editFormConfig().suppress() == QgsEditFormConfig.SuppressOn:
                self.__layer.addFeature(pt_feat)
            else:
                self.__iface.openFeatureForm(self.__layer, pt_feat)

        if withVertex:
            line_v2.insertVertex(vertex_id, vertex_v2)
            self.__lastLayer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2))

            found_features = self.__lastLayer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "One feature at a time"),
                                                          level=QgsMessageBar.INFO)
                else:
                    self.__selectedFeature = found_features[0]
            else:
                self.__iface.messageBar().pushMessage(QCoreApplication.translate("VDLTools", "No more feature selected"),
                                                          level=QgsMessageBar.INFO)

        self.__iface.mapCanvas().refresh()

        self.__done()
        self.__findVertex = True
Example #12
0
class nominatim_dlg(QDockWidget, FORM_CLASS):
    """
    Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock
    """
    def eventFilter(self, obj, event):
        typ = event.type()
        if typ == event.Leave:
            try:
                self.plugin.canvas.scene().removeItem(self.rubber)
            except:
                pass

        return False

    def __init__(self, parent, plugin):
        self.plugin = plugin
        QDockWidget.__init__(self, parent)
        self.setupUi(self)

        self.btnApply.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/arrow_green.png")))
        self.btnMask.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/add_mask.png")))
        self.btnLayer.setIcon(
            QIcon(str(DIR_PLUGIN_ROOT / "resources/add_layer.png")))
        self.btnHelp.setIcon(
            QIcon(QgsApplication.iconPath("mActionHelpContents.svg")))

        self.tableResult.installEventFilter(self)  # cf. eventFilter method
        self.tableResult.cellDoubleClicked.connect(self.onChoose)
        self.tableResult.cellEntered.connect(self.cellEntered)

        self.editSearch.returnPressed.connect(self.onReturnPressed)
        self.btnSearch.clicked.connect(self.onReturnPressed)
        self.btnApply.clicked.connect(self.onApply)
        self.btnHelp.clicked.connect(
            lambda: showPluginHelp(filename="../doc/index"))

        self.btnLocalize.clicked.connect(self.doLocalize)
        self.btnMask.clicked.connect(self.onMask)
        self.btnLayer.clicked.connect(self.onLayer)

        self.singleLayerId = {
            QgsWkbTypes.PolygonGeometry: None,
            QgsWkbTypes.LineGeometry: None,
            QgsWkbTypes.PointGeometry: None,
        }
        self.singleLayerName = {
            QgsWkbTypes.PolygonGeometry: "OSM Place Search Polygons",
            QgsWkbTypes.LineGeometry: "OSM Place Search Lines",
            QgsWkbTypes.PointGeometry: "OSM Place Search Points",
        }
        self.memoryLayerType = {
            QgsWkbTypes.PolygonGeometry: "MultiPolygon",
            QgsWkbTypes.LineGeometry: "MultiLineString",
            QgsWkbTypes.PointGeometry: "Point",
        }

        try:
            self.cbExtent.setChecked(tools.limitSearchToExtent)
        except:
            self.cbExtent.setChecked(tools.limitSearchToExtent)

        self.currentExtent = self.plugin.canvas.extent()

        self.tableResult.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)

        try:
            self.editSearch.setText(self.plugin.lastSearch)
        except:
            pass

    def cellEntered(self, row, col):
        item = self.tableResult.item(row, 0)

        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
            self.showItem(item)
        except:
            pass

    def onLayer(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doLayer(item)

    def onMask(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doMask(item)

    def populateRow(self, item, idx):
        id = item["place_id"]
        name = item["display_name"]

        try:
            className = QApplication.translate("nominatim", item["class"],
                                               None)
        except:
            className = ""

        try:
            typeName = QApplication.translate("nominatim", item["type"], None)
        except:
            typeName = ""

        try:
            wkt = item["geotext"]
        except:
            wkt = None

        try:
            osm_type = item["osm_type"]
        except:
            osm_type = None

        bbox = {}
        if osm_type == "node":
            lat = item["lat"]
            lng = item["lon"]

            poFD = ogr.FeatureDefn("Point")
            poFD.SetGeomType(ogr.wkbPoint)

            oFLD = ogr.FieldDefn("id", ogr.OFTString)
            poFD.AddFieldDefn(oFLD)
            oFLD = ogr.FieldDefn("name", ogr.OFTString)
            poFD.AddFieldDefn(oFLD)

            ogrFeature = ogr.Feature(poFD)
            wkt = "POINT({} {})".format(lng, lat)
            ogrGeom = ogr.CreateGeometryFromWkt(wkt)
        else:
            try:
                bbox = item["boundingbox"]

                poFD = ogr.FeatureDefn("Rectangle")
                poFD.SetGeomType(ogr.wkbPolygon)

                oFLD = ogr.FieldDefn("id", ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn("name", ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                if wkt is None:
                    wkt = "POLYGON(({b[2]} {b[0]}, {b[2]} {b[1]}, {b[3]} {b[1]}, {b[3]} {b[0]}, {b[2]} {b[0]}))".format(
                        b=bbox)

                ogrGeom = ogr.CreateGeometryFromWkt(wkt)
            except:
                lat = item["lat"]
                lng = item["lon"]

                poFD = ogr.FeatureDefn("Point")
                poFD.SetGeomType(ogr.wkbPoint)

                oFLD = ogr.FieldDefn("id", ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn("name", ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                wkt = "POINT({} {})".format(lng, lat)
                ogrGeom = ogr.CreateGeometryFromWkt(wkt)

        ogrFeature.SetGeometry(ogrGeom)
        ogrFeature.SetFID(int(idx + 1))
        ogrFeature.SetField(str("id"), str(id))
        ogrFeature.SetField(str("name"), name)

        item = QTableWidgetItem(name)
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setData(Qt.UserRole, ogrFeature)
        self.tableResult.setItem(idx, 0, item)

        itemLibelle = QTableWidgetItem(className)
        itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 1, itemLibelle)

        itemType = QTableWidgetItem(typeName)
        itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 2, itemType)

    def populateTable(self, r):
        idx = 0
        self.tableResult.clearContents()
        self.tableResult.setRowCount(len(r))
        for item in r:
            self.populateRow(item, idx)
            idx = idx + 1

    def doLocalize(self):
        try:
            # center
            bbox = self.plugin.canvas.extent()
            sourceCrs = self.plugin.canvas.mapSettings().destinationCrs()
            targetCrs = QgsCoordinateReferenceSystem("EPSG:4326")
            xform = QgsCoordinateTransform(sourceCrs, targetCrs,
                                           QgsProject.instance())
            bbox = xform.transform(bbox)

            params = {
                "lon": str(bbox.center().x()),
                "lat": str(bbox.center().y()),
                "zoom": "10",
            }
            r = tools.osmFindNearbyJSON(params, tools.gnOptions)
            if r != None:
                self.populateTable(r)
            else:
                self.tableResult.clearContents()

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, "Extensions")
            pass

    def search(self, txt):
        try:
            self.plugin.lastSearch = self.editSearch.text()
            tools.limitSearchToExtent = self.cbExtent.isChecked()
            return tools.osmSearch(self.plugin.iface.mapCanvas(), txt)

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, "Extensions")

        return None

    def onReturnPressed(self):
        txt = self.editSearch.text().strip()
        r = self.search(txt)
        if r != None:
            self.populateTable(r)
        else:
            self.tableResult.clearContents()

    def onChoose(self, row, col):
        item = self.tableResult.item(row, 0)
        self.go(item)

    def onApply(self):
        for item in self.tableResult.selectedItems():
            self.go(item)
            break

    def transform(self, geom):
        sourceSRS = QgsCoordinateReferenceSystem("EPSG:4326")
        mapCrs = self.plugin.canvas.mapSettings().destinationCrs()
        trsf = QgsCoordinateTransform(sourceSRS, mapCrs, QgsProject.instance())
        try:
            geom.transform(trsf)
        except TypeError:
            QgsMessageLog.logMessage(
                "Nominatim - transformation error. Check map projection.",
                "Extensions")

    def getBBox(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint:
            mapextent = self.plugin.canvas.extent()
            ww = mapextent.width() / 100
            mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

            x = geom.boundingBox().center().x()
            y = geom.boundingBox().center().y()

            ww = 50.0
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet:
                ww = 150
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees:
                ww = 0.0005

            bbox = QgsRectangle(x - 10 * ww, y - 10 * ww, x + 10 * ww,
                                y + 10 * ww)
            return bbox
        else:
            bbox = geom.boundingBox()
            rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(),
                                      bbox.xMaximum(), bbox.yMaximum())
            return rubberRect

    def showItem(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint:
            self.rubber = QgsRubberBand(self.plugin.canvas,
                                        QgsWkbTypes.PointGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setIcon(self.rubber.ICON_CIRCLE)
            self.rubber.setIconSize(15)
            self.rubber.setWidth(2)
            self.rubber.setToGeometry(geom, None)
        else:
            # dont show if it is larger than the canvas
            if self.plugin.canvas.extent().contains(geom.boundingBox()):
                pass
            else:
                geom = geom.intersection(
                    QgsGeometry.fromRect(self.plugin.canvas.extent()))

            self.rubber = QgsRubberBand(self.plugin.canvas,
                                        QgsWkbTypes.PolygonGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setWidth(4)
            self.rubber.setToGeometry(geom, None)

    def go(self, item, zoom=True):
        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
        except:
            pass

        if zoom:
            bbox = self.getBBox(item)
            self.plugin.canvas.setExtent(bbox)

        self.plugin.canvas.refresh()
        self.showItem(item)

    def addNewLayer(self, layerName, typ, fields):
        vl = QgsVectorLayer(self.memoryLayerType[typ], layerName, "memory")
        if vl:
            vl.setProviderEncoding("UTF-8")
            pr = vl.dataProvider()
            pr.addAttributes(fields.toList())
            vl.setCrs(self.plugin.canvas.mapSettings().destinationCrs())
            QgsProject.instance().addMapLayer(vl)
            renderer = vl.renderer()
            s = renderer.symbol()
            s.setOpacity(0.85)

        return vl

    def doLayer(self, item, singleLayer=None):
        if singleLayer is None:
            singleLayer = self.plugin.singleLayer

        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        fields = QgsFields()
        fields.append(QgsField("id", QVariant.String))
        fields.append(QgsField("name", QVariant.String))
        fet = QgsFeature()
        fet.initAttributes(2)
        fet.setFields(fields)
        fet.setGeometry(geom)
        fet.setAttribute("id", (ogrFeature.GetFieldAsString("id")))
        fet.setAttribute("name", (ogrFeature.GetFieldAsString("name")))

        vl = None
        if not singleLayer:
            layerId = self.singleLayerId[geom.type()]
            layerName = self.singleLayerName[geom.type()]
            vl = QgsProject.instance().mapLayer(layerId)
            if vl is None:
                vl = self.addNewLayer(layerName, geom.type(), fields)
                if vl:
                    self.singleLayerId[geom.type()] = vl.id()
        else:
            layerName = "OSM " + ogrFeature.GetFieldAsString("id")
            vl = self.addNewLayer(layerName, geom.type(), fields)

        if vl is not None:
            pr = vl.dataProvider()
            vl.startEditing()
            pr.addFeatures([fet])
            vl.commitChanges()

            # mise a jour etendue de la couche
            vl.updateExtents()
            """
            layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl)
            if layerTree:
                self.plugin.iface.layerTreeView().layerTreeModel().refreshLayerLegend(
                    layerTree
                )  # Refresh legend
            """
            self.go(item, False)

            return vl

    def doMask(self, item):
        mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

        ogrFeature = item.data(Qt.UserRole)
        layerName = "OSM " + ogrFeature.GetFieldAsString("id")
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if geom.type() == QgsWkbTypes.PolygonGeometry:
            try:
                try:
                    from mask import aeag_mask
                except:
                    from mask_plugin import aeag_mask

                aeag_mask.do(mapcrs, {geom}, "Mask " + layerName)
                self.go(item)

            except:
                maskLayer = self.doLayer(item, True)
                maskLayer.loadNamedStyle(
                    str(DIR_PLUGIN_ROOT / "resources" / "mask.qml"))
                maskLayer.triggerRepaint()
Example #13
0
class SoLocatorFilter(QgsLocatorFilter):

    HEADERS = {b'User-Agent': b'Mozilla/5.0 QGIS SoLocator Filter'}

    message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget)

    def __init__(self, iface: QgisInterface = None):
        """"
        :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise
        """
        super().__init__()

        self.iface = iface
        self.settings = Settings()

        #  following properties will only be used in main thread
        self.rubber_band = None
        self.map_canvas: QgsMapCanvas = None
        self.transform_ch = None
        self.current_timer = None
        self.result_found = False
        self.nam_fetch_feature = None

        if iface is not None:
            # happens only in main thread
            self.map_canvas = iface.mapCanvas()
            self.map_canvas.destinationCrsChanged.connect(
                self.create_transforms)

            self.rubber_band = QgsRubberBand(self.map_canvas,
                                             QgsWkbTypes.PolygonGeometry)
            self.rubber_band.setColor(QColor(255, 50, 50, 200))
            self.rubber_band.setFillColor(QColor(255, 255, 50, 160))
            self.rubber_band.setBrushStyle(Qt.SolidPattern)
            self.rubber_band.setLineStyle(Qt.SolidLine)
            self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE)
            self.rubber_band.setIconSize(15)
            self.rubber_band.setWidth(4)
            self.rubber_band.setBrushStyle(Qt.NoBrush)

            self.create_transforms()

    def name(self):
        return 'SoLocator'

    def clone(self):
        return SoLocatorFilter()

    def priority(self):
        return QgsLocatorFilter.Highest

    def displayName(self):
        return 'SoLocator'

    def prefix(self):
        return 'sol'

    def clearPreviousResults(self):
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.PointGeometry)

        if self.current_timer is not None:
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def hasConfigWidget(self):
        return True

    def openConfigWidget(self, parent=None):
        dlg = ConfigDialog(parent)
        dlg.exec_()

    def create_transforms(self):
        # this should happen in the main thread
        src_crs_ch = QgsCoordinateReferenceSystem('EPSG:2056')
        assert src_crs_ch.isValid()
        dst_crs = self.map_canvas.mapSettings().destinationCrs()
        self.transform_ch = QgsCoordinateTransform(src_crs_ch, dst_crs,
                                                   QgsProject.instance())

    def enabled_dataproducts(self):
        categories = DATA_PRODUCTS.keys()
        skipped = self.settings.value('skipped_dataproducts')
        return ','.join(list(filter(lambda id: id not in skipped, categories)))

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url.url()

    def fetchResults(self, search: str, context: QgsLocatorContext,
                     feedback: QgsFeedback):
        try:
            self.dbg_info("start solocator search...")

            if len(search) < 3:
                return

            self.result_found = False

            params = {
                'searchtext': str(search),
                'filter': self.enabled_dataproducts(),
                'limit': str(self.settings.value('results_limit'))
            }

            nam = NetworkAccessManager()
            feedback.canceled.connect(nam.abort)
            url = self.url_with_param(SEARCH_URL, params)
            self.dbg_info(url)
            try:
                (response, content) = nam.request(url,
                                                  headers=self.HEADERS,
                                                  blocking=True)
                self.handle_response(response, search)
            except RequestsExceptionUserAbort:
                pass
            except RequestsException as err:
                self.info(err, Qgis.Info)

            if not self.result_found:
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = self.tr('No result found.')
                result.userData = NoResult
                self.resultFetched.emit(result)

        except Exception as e:
            self.info(e, Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def data_product_qgsresult(self, data: dict, sub_layer: bool, score: float,
                               stacktype) -> QgsLocatorResult:
        result = QgsLocatorResult()
        result.filter = self
        result.displayString = '{prefix}{title}'.format(
            prefix=' ↳ ' if sub_layer else '', title=data['display'])
        if stacktype == 'background':
            result.group = 'Hintergrundkarten'
        else:
            loading_mode: LoadingMode = self.settings.value(
                'default_layer_loading_mode')
            result.group = 'Vordergrundkarten (Doppelklick: {normal}, Ctrl-Doppelklick: {alt})'.format(
                normal=loading_mode, alt=loading_mode.alternate_mode())
        result.userData = DataProductResult(
            type=data['type'],
            dataproduct_id=data['dataproduct_id'],
            display=data['display'],
            dset_info=data['dset_info'],
            stacktype=stacktype,
            sublayers=data.get('sublayers', None))
        data_product = 'dataproduct'
        data_type = data['type']
        result.icon, result.description = dataproduct2icon_description(
            data_product, data_type)
        result.score = score
        return result

    def handle_response(self, response, search_text: str):
        try:
            if response.status_code != 200:
                if not isinstance(response.exception,
                                  RequestsExceptionUserAbort):
                    self.info("Error in main response with status code: "
                              "{} from {}".format(response.status_code,
                                                  response.url))
                return

            data = json.loads(response.content.decode('utf-8'))

            # Since results are ordered by score (0 to 1)
            # we use an ordering score to keep the same order than the one from the remote service
            score = 1

            # sub-filtering
            # dbg_info(data['result_counts'])
            if len(data['result_counts']) > 1:
                for _filter in data['result_counts']:
                    result = QgsLocatorResult()
                    result.filter = self
                    result.group = 'Suche verfeinern'
                    result.displayString = _filter['filterword']
                    if _filter['count']:
                        result.displayString += ' ({})'.format(
                            _filter['count'])
                    self.dbg_info(_filter)
                    result.icon, _ = dataproduct2icon_description(
                        _filter['dataproduct_id'], 'datasetview')
                    result.userData = FilterResult(_filter['filterword'],
                                                   search_text)
                    result.score = score
                    self.resultFetched.emit(result)
                    score -= 0.001

            for res in data['results']:
                # dbg_info(res)

                result = QgsLocatorResult()
                result.filter = self

                if 'feature' in res.keys():
                    f = res['feature']
                    # dbg_info("feature: {}".format(f))
                    result.displayString = f['display']
                    result.group = 'Orte'
                    result.userData = FeatureResult(
                        dataproduct_id=f['dataproduct_id'],
                        id_field_name=f['id_field_name'],
                        id_field_type=f['id_field_type'],
                        feature_id=f['feature_id'])
                    data_product = f['dataproduct_id']
                    data_type = None
                    result.icon, result.description = dataproduct2icon_description(
                        data_product, data_type)
                    result.score = score
                    self.resultFetched.emit(result)
                    score -= 0.001

                elif 'dataproduct' in res.keys():
                    dp = res['dataproduct']
                    # self.dbg_info("data_product: {}".format(dp))
                    result = self.data_product_qgsresult(
                        dp, False, score, dp['stacktype'])
                    self.resultFetched.emit(result)
                    score -= 0.001

                    # also give sublayers
                    for layer in dp.get('sublayers', []):
                        always_show_sublayers = True
                        if always_show_sublayers or search_text.lower(
                        ) in layer['display'].lower():
                            result = self.data_product_qgsresult(
                                layer, True, score, dp['stacktype'])
                            self.resultFetched.emit(result)
                            score -= 0.001

                else:
                    continue

                self.result_found = True

        except Exception as e:
            self.info(str(e), Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            self.info(
                '{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno),
                Qgis.Critical)
            self.info(
                traceback.print_exception(exc_type, exc_obj, exc_traceback),
                Qgis.Critical)

    def triggerResult(self, result: QgsLocatorResult):
        # this is run in the main thread, i.e. map_canvas is not None
        self.clearPreviousResults()

        ctrl_clicked = Qt.ControlModifier == QApplication.instance(
        ).queryKeyboardModifiers()
        self.dbg_info(("CTRL pressed: {}".format(ctrl_clicked)))

        user_data = self.get_user_data(result)
        if type(user_data) == NoResult:
            pass
        elif type(user_data) == FilterResult:
            self.filtered_search(user_data)
        elif type(user_data) == FeatureResult:
            self.fetch_feature(user_data)
        elif type(user_data) == DataProductResult:
            self.fetch_data_product(user_data, ctrl_clicked)
        else:
            self.info('Incorrect result. Please contact support',
                      Qgis.Critical)

    def filtered_search(self, filter_result: FilterResult):
        search_text = '{prefix} {filter_word}: {search}'.format(
            prefix=self.activePrefix(),
            filter_word=filter_result.filter_word,
            search=filter_result.search)
        # Compatibility for QGIS < 3.10
        # TODO: remove
        try:
            self.iface.locatorSearch(search_text)
        except AttributeError:
            for w in self.iface.mainWindow().findChildren(QgsFilterLineEdit):
                if hasattr(w.parent(), 'search') and hasattr(
                        w.parent(), 'invalidateResults'):
                    w.setText(search_text)
                    w.parent().setFocus(True)
                    return
            raise NameError('Locator not found')

    def highlight(self, geometry: QgsGeometry):
        self.clearPreviousResults()
        if geometry is None:
            return

        self.rubber_band.reset(geometry.type())
        self.rubber_band.addGeometry(geometry, None)

        rect = geometry.boundingBox()
        if not self.settings.value('keep_scale'):
            if rect.isEmpty():
                current_extent = self.map_canvas.extent()
                rect = current_extent.scaled(
                    self.settings.value('point_scale') /
                    self.map_canvas.scale(), rect.center())
            else:
                rect.scale(4)
        self.map_canvas.setExtent(rect)
        self.map_canvas.refresh()

        self.current_timer = QTimer()
        self.current_timer.timeout.connect(self.clearPreviousResults)
        self.current_timer.setSingleShot(True)
        self.current_timer.start(5000)

    def fetch_feature(self, feature: FeatureResult):
        self.dbg_info(feature)
        url = '{url}/{dataset}/{id}'.format(url=FEATURE_URL,
                                            dataset=feature.dataproduct_id,
                                            id=feature.feature_id)
        self.nam_fetch_feature = NetworkAccessManager()
        self.dbg_info(url)
        self.nam_fetch_feature.finished.connect(self.parse_feature_response)
        self.nam_fetch_feature.request(url,
                                       headers=self.HEADERS,
                                       blocking=False)

    def parse_feature_response(self, response):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info("Error in feature response with status code: "
                          "{} from {}".format(response.status_code,
                                              response.url))
            return

        data = json.loads(response.content.decode('utf-8'))
        self.dbg_info(data.keys())
        self.dbg_info(data['properties'])
        self.dbg_info(data['geometry'])
        self.dbg_info(data['crs'])
        self.dbg_info(data['type'])

        assert data['crs']['properties'][
            'name'] == 'urn:ogc:def:crs:EPSG::2056'

        geometry_type = data['geometry']['type']
        geometry = QgsGeometry()

        if geometry_type.lower() == 'point':
            geometry = QgsGeometry.fromPointXY(
                QgsPointXY(data['geometry']['coordinates'][0],
                           data['geometry']['coordinates'][1]))

        elif geometry_type.lower() == 'polygon':
            rings = data['geometry']['coordinates']
            for r in range(0, len(rings)):
                for p in range(0, len(rings[r])):
                    rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1])
            geometry = QgsGeometry.fromPolygonXY(rings)

        elif geometry_type.lower() == 'multipolygon':
            islands = data['geometry']['coordinates']
            for i in range(0, len(islands)):
                for r in range(0, len(islands[i])):
                    for p in range(0, len(islands[i][r])):
                        islands[i][r][p] = QgsPointXY(islands[i][r][p][0],
                                                      islands[i][r][p][1])
            geometry = QgsGeometry.fromMultiPolygonXY(islands)

        else:
            # SoLocator does not handle {geometry_type} yet. Please contact support
            self.info(
                'SoLocator unterstützt den Geometrietyp {geometry_type} nicht.'
                ' Bitte kontaktieren Sie den Support.'.format(
                    geometry_type=geometry_type), Qgis.Warning)

        geometry.transform(self.transform_ch)
        self.highlight(geometry)

    def fetch_data_product(self, product: DataProductResult,
                           alternate_mode: bool):
        self.dbg_info(product)
        url = '{url}/{dataproduct_id}'.format(
            url=DATA_PRODUCT_URL, dataproduct_id=product.dataproduct_id)
        self.nam_fetch_feature = NetworkAccessManager()
        self.dbg_info(url)
        is_background = product.stacktype == 'background'
        self.dbg_info('is_background {}'.format(is_background))
        self.nam_fetch_feature.finished.connect(
            lambda response: self.parse_data_product_response(
                response, is_background, alternate_mode))
        self.nam_fetch_feature.request(url,
                                       headers=self.HEADERS,
                                       blocking=False)

    def parse_data_product_response(self, response, is_background: bool,
                                    alternate_mode: bool):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                self.info("Error in feature response with status code: "
                          "{} from {}".format(response.status_code,
                                              response.url))
            return

        data = json.loads(response.content.decode('utf-8'))
        LayerLoader(data, self.iface, is_background, alternate_mode)

    def info(self, msg="", level=Qgis.Info):
        self.logMessage(str(msg), level)

    def dbg_info(self, msg=""):
        if DEBUG:
            self.info(msg)

    def get_user_data(self, result):
        if hasattr(result, 'getUserData'):
            return result.getUserData()
        else:
            return result.userData
Example #14
0
class Geo360Dialog(QWidget, Ui_orbitalDialog):
    """Geo360 Dialog Class"""
    def __init__(self, iface, parent=None, featuresId=None, layer=None):

        QDialog.__init__(self)

        self.setupUi(self)
        self.s = QSettings()

        self.plugin_path = os.path.dirname(os.path.realpath(__file__))

        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.parent = parent

        # Orientation from image
        self.yaw = math.pi
        self.bearing = None

        self.layer = layer
        self.featuresId = featuresId

        self.actualPointDx = None
        self.actualPointSx = None
        self.actualPointOrientation = None

        self.selected_features = qgsutils.getToFeature(self.layer,
                                                       self.featuresId)

        # Get image path
        self.current_image = self.GetImage()

        # Create Viewer
        self.CreateViewer()
        self.RestoreSize()
        # Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"There is no associated image.")
            self.resetQgsRubberBand()
            time.sleep(1)
            self.ChangeUrlViewer(config.DEFAULT_EMPTY)
            return

        # Copy file to local server
        self.CopyFile(self.current_image)

        # Set RubberBand
        self.resetQgsRubberBand()
        self.setOrientation()
        self.setPosition()

    def SetInitialYaw(self):
        ''' Set Initial Yaw '''
        self.bearing = self.selected_features.attribute(config.column_yaw)
        self.view.browser.GetMainFrame().ExecuteFunction(
            "InitialYaw", self.bearing)
        return

    def CreateViewer(self):
        ''' Create Viewer '''
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Create viewer",
                                       onlyLog=True)

        self.view = CefWidget(self)
        self.ViewerLayout.addWidget(self.view)
        self.view.embedBrowser()
        return

    def RemoveImage(self):
        ''' Remove Image '''
        try:
            os.remove(self.plugin_path + "\\viewer\\image.jpg")
        except OSError:
            pass

    def CopyFile(self, src):
        ''' Copy Image File in Local Server '''
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Copiar imagem",
                                       onlyLog=True)

        src_dir = src
        dst_dir = self.plugin_path + "\\viewer"

        # Copy image in local folder
        # Uncomment for large images if viewer is blank screen
        img = Image.open(src_dir)
        newwidth = 8000
        dst_dir = dst_dir + "\\image.jpg"

        try:
            os.remove(dst_dir)
        except OSError:
            pass

        width, _ = img.size

        if width > newwidth:
            wpercent = (newwidth / float(img.size[0]))
            hsize = int((float(img.size[1]) * float(wpercent)))
            img = img.resize((newwidth, hsize), Image.ANTIALIAS)
            img.save(dst_dir, optimize=True, quality=95)

        # Comment for large images if viewer is blank screen
        else:
            shutil.copy(src_dir, dst_dir)

        return

    def RestoreSize(self):
        ''' Restore Dialog Size '''
        dw = self.s.value("EquirectangularViewer/width")
        dh = self.s.value("EquirectangularViewer/height")

        if dw is None:
            return
        size = self.size()

        anim = QPropertyAnimation(self, b'size', self)
        anim.setStartValue(size)
        anim.setEndValue(QSize(int(dw), int(dh)))
        anim.setDuration(1)
        anim.start()
        return

    def SaveSize(self):
        ''' Save Dialog Size '''
        dw = self.width()
        dh = self.height()
        self.s.setValue("EquirectangularViewer/width", dw)
        self.s.setValue("EquirectangularViewer/height", dh)
        return

    def GetImage(self):
        ''' Get Selected Image '''
        try:
            path = qgsutils.getAttributeFromFeature(self.selected_features,
                                                    config.column_name)
            if not os.path.isabs(path):  # Relative Path to Project
                path_project = QgsProject.instance().readPath("./")
                path = os.path.normpath(os.path.join(path_project, path))
        except Exception:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"Column not found.")
            return

        qgsutils.showUserAndLogMessage(u"Information: ",
                                       str(path),
                                       onlyLog=True)
        return path

    def ChangeUrlViewer(self, new_url):
        ''' Change Url Viewer '''
        self.view.browser.GetMainFrame().ExecuteJavascript(
            "window.location='%s'" % new_url)
        return

    def ReloadView(self, newId):
        ''' Reaload Image viewer '''
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                            | Qt.WindowActive)
        # this will activate the window
        self.activateWindow()
        self.selected_features = qgsutils.getToFeature(self.layer, newId)

        self.current_image = self.GetImage()

        # Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"There is no associated image.")
            self.ChangeUrlViewer(config.DEFAULT_EMPTY)
            self.resetQgsRubberBand()
            return

        # Set RubberBand
        self.resetQgsRubberBand()
        self.setOrientation()
        self.setPosition()

        # Copy file to local server
        self.CopyFile(self.current_image)

        self.ChangeUrlViewer(config.DEFAULT_URL)
        return

    def ResizeDialog(self):
        ''' Expanded/Decreased Dialog '''
        sender = QObject.sender(self)

        w = self.width()
        h = self.height()

        size = self.size()
        anim = QPropertyAnimation(self, b'size', self)
        anim.setStartValue(size)

        if sender.objectName() == "btn_ZoomOut":
            anim.setEndValue(QSize(w - 50, h - 50))
        else:
            anim.setEndValue(QSize(w + 50, h + 50))

        anim.setDuration(300)
        anim.start()
        return

    def GetBackNextImage(self):
        ''' Get to Back Image '''
        sender = QObject.sender(self)

        lys = self.canvas.layers()  # Check if mapa foto is loaded
        if len(lys) == 0:
            qgsutils.showUserAndLogMessage(
                u"Information: ", u"You need to upload the photo layer.")
            return

        for layer in lys:
            if layer.name() == config.layer_name:
                self.encontrado = True
                self.iface.setActiveLayer(layer)

                f = self.selected_features

                ac_lordem = f.attribute(config.column_order)

                if sender.objectName() == "btn_back":
                    new_lordem = int(ac_lordem) - 1
                else:
                    new_lordem = int(ac_lordem) + 1

                # Filter mapa foto layer
                ids = [
                    feat.id() for feat in layer.getFeatures(QgsFeatureRequest(
                    ).setFilterExpression(config.column_order + " ='" +
                                          str(new_lordem) + "'"))
                ]

                if len(ids) == 0:
                    qgsutils.showUserAndLogMessage(
                        u"Information: ",
                        u"There is no superiority that follows.")
                    # Filter mapa foto layer
                    ids = [
                        feat.id()
                        for feat in layer.getFeatures(QgsFeatureRequest(
                        ).setFilterExpression(config.column_order + " ='" +
                                              str(ac_lordem) + "'"))
                    ]
                    # Update selected feature
                    self.ReloadView(ids[0])
                    return

                self.ReloadView(ids[0])

        if self.encontrado is False:
            qgsutils.showUserAndLogMessage(
                u"Information: ", u"You need to upload the photo layer.")

        return

    def FullScreen(self, value):
        ''' FullScreen action button '''
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Fullscreen.",
                                       onlyLog=True)
        if (value):
            self.showFullScreen()
        else:
            self.showNormal()
        return

    @staticmethod
    def ActualOrientation(yaw):
        ''' Get Actual yaw '''
        geo360Plugin = qgis.utils.plugins["EquirectangularViewer"]
        if geo360Plugin is not None:
            geo360Dialog = qgis.utils.plugins["EquirectangularViewer"].dlg
            if geo360Dialog is not None:
                geo360Dialog.UpdateOrientation(yaw=float(yaw))
        return

    def UpdateOrientation(self, yaw=None):
        ''' Update Orientation '''
        self.bearing = self.selected_features.attribute(config.column_yaw)
        try:
            self.actualPointOrientation.reset()
        except Exception:
            pass

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QgsWkbTypes.LineGeometry)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)
        self.actualPointOrientation.addPoint(self.actualPointDx)

        # End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPointXY(
            float(A1x), float(A1y)))

        # Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()

        self.actualPointOrientation.setToGeometry(
            self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle),
            self.dumLayer)

    def setOrientation(self, yaw=None):
        ''' Set Orientation in the firt time '''
        self.bearing = self.selected_features.attribute(config.column_yaw)

        self.actualPointDx = self.selected_features.geometry().asPoint()

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QgsWkbTypes.LineGeometry)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)

        self.actualPointOrientation.addPoint(self.actualPointDx)

        # End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPointXY(
            float(A1x), float(A1y)))
        # Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()

        self.rotateTool = transformGeometry()
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        self.dumLayer = QgsVectorLayer("Point?crs=" + epsg, "temporary_points",
                                       "memory")
        self.actualPointOrientation.setToGeometry(
            self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle),
            self.dumLayer)

    def setPosition(self):
        ''' Set RubberBand Position '''
        self.actualPointDx = self.selected_features.geometry().asPoint()

        self.positionDx = QgsRubberBand(self.iface.mapCanvas(),
                                        QgsWkbTypes.PointGeometry)
        self.positionDx.setWidth(6)
        self.positionDx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionDx.setIconSize(6)
        self.positionDx.setColor(Qt.black)
        self.positionSx = QgsRubberBand(self.iface.mapCanvas(),
                                        QgsWkbTypes.PointGeometry)
        self.positionSx.setWidth(5)
        self.positionSx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionSx.setIconSize(4)
        self.positionSx.setColor(Qt.blue)
        self.positionInt = QgsRubberBand(self.iface.mapCanvas(),
                                         QgsWkbTypes.PointGeometry)
        self.positionInt.setWidth(5)
        self.positionInt.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionInt.setIconSize(3)
        self.positionInt.setColor(Qt.white)

        self.positionDx.addPoint(self.actualPointDx)
        self.positionSx.addPoint(self.actualPointDx)
        self.positionInt.addPoint(self.actualPointDx)

    def closeEvent(self, _):
        ''' Close dialog '''
        self.resetQgsRubberBand()
        self.canvas.refresh()
        self.iface.actionPan().trigger()
        self.SaveSize()
        self.parent.dlg = None
        self.RemoveImage()
        return

    def resetQgsRubberBand(self):
        ''' Remove RubbeBand '''
        try:
            self.positionSx.reset()
            self.positionInt.reset()
            self.positionDx.reset()
            self.actualPointOrientation.reset()
        except Exception:
            None
Example #15
0
class ProfileTool(QgsMapTool):
    """
    Tool class for making a line elevation profile
    """

    ALT_TOLERANCE = 0.0005
    SEARCH_TOLERANCE = 0.001

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/profile_icon.png'
        self.text = QCoreApplication.translate("VDLTools", "Profile of a line")
        self.__lineLayer = None
        self.setCursor(Qt.ArrowCursor)
        self.__isChoosed = False
        self.__lastFeatureId = None
        self.__lastFeature = None
        self.__dockWdg = None
        self.__layDlg = None
        self.__msgDlg = None
        self.__confDlg = None
        self.__zeroDlg = None
        self.__points = None
        self.__layers = None
        self.__features = None
        self.__inSelection = False
        self.__selectedIds = None
        self.__selectedStarts = None
        self.__selectedDirections = None
        self.__startVertex = None
        self.__endVertex = None
        self.__rubberSit = None
        self.__rubberDif = None
        self.ownSettings = None
        self.__usedMnts = None
        self.__isfloating = False
        self.__dockGeom = None

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__dockWdg = ProfileDockWidget(self.__iface, self.__dockGeom)
        if self.__isfloating:
            self.__dockWdg.show()
        else:
           self.__iface.addDockWidget(Qt.BottomDockWidgetArea, self.__dockWdg)
        self.__dockWdg.closeSignal.connect(self.__closed)
        self.__rubberSit = QgsRubberBand(self.canvas(), QGis.Point)
        self.__rubberDif = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubberSit.setColor(color)
        self.__rubberSit.setIcon(4)
        self.__rubberSit.setIconSize(20)
        self.__rubberDif.setColor(color)
        self.__rubberDif.setIcon(2)
        self.__rubberDif.setIconSize(20)

    def __closed(self):
        """
        When the dock is closed
        """
        self.__dockGeom = self.__dockWdg.geometry()
        self.__isfloating = self.__dockWdg.isFloating()
        self.__cancel()
        self.__iface.actionPan().trigger()

    def deactivate(self):
        """
        When the action is deselected
        """
        self.canvas().scene().removeItem(self.__rubberDif)
        self.__rubberDif = None
        self.canvas().scene().removeItem(self.__rubberSit)
        self.__rubberSit = None
        if self.__dockWdg is not None:
            self.__dockWdg.close()
        QgsMapTool.deactivate(self)

    def __cancel(self):
        """
        To cancel used variables
        """
        if self.__lineLayer is not None:
            self.__lineLayer.removeSelection()
        self.__lastFeatureId = None
        self.__lastFeature = None
        self.__selectedIds = None
        self.__selectedDirections = None
        self.__startVertex = None
        self.__endVertex = None
        self.__inSelection = False
        self.__layDlg = None
        self.__msgDlg = None
        self.__confDlg = None
        self.__zeroDlg = None
        self.__isChoosed = False

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type() == QgsMapLayer.VectorLayer and QGis.fromOldWkbType(layer.wkbType()) == \
                QgsWKBTypes.LineStringZ:
            self.__lineLayer = layer
            self.action().setEnabled(True)
            return
        self.action().setEnabled(False)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()
        if self.__dockWdg is not None:
            self.__dockWdg.close()
        self.__lineLayer = None

    def __setLayerDialog(self):
        """
        To create a Profile Layers Dialog
        """
        otherLayers = self.__lineVertices(True)
        with_mnt = True
        if self.ownSettings is None or self.ownSettings.mntUrl is None \
                or self.ownSettings.mntUrl == "":
            with_mnt = False
        if not with_mnt and len(otherLayers) == 0:
            self.__layers = []
            self.__layOk()
        else:
            self.__layDlg = ProfileLayersDialog(otherLayers, with_mnt)
            self.__layDlg.rejected.connect(self.__cancel)
            self.__layDlg.okButton().clicked.connect(self.__onLayOk)
            self.__layDlg.cancelButton().clicked.connect(self.__onLayCancel)
            self.__layDlg.show()

    def __setMessageDialog(self, situations, differences, names):
        """
        To create a Profile Message Dialog
        :param situations: elevation differences between line and points
        :param differences: elevation differences between lines
        :param names: layers names
        """
        self.__msgDlg = ProfileMessageDialog(situations, differences, names, self.__points)
        self.__msgDlg.rejected.connect(self.__checkZeros)
        self.__msgDlg.passButton().clicked.connect(self.__onMsgPass)
        self.__msgDlg.onLineButton().clicked.connect(self.__onMsgLine)
        self.__msgDlg.onPointsButton().clicked.connect(self.__onMsgPoints)

    def __setConfirmDialog(self, origin):
        """
        To create a Profile Confirm Dialog
        :param origin: '0' if we copy points elevations to line, '1' if we copy line elevation to points
        """
        self.__confDlg = ProfileConfirmDialog()
        if origin == 0 and not self.__lineLayer.isEditable():
            self.__confDlg.setMessage(
                QCoreApplication.translate("VDLTools", "Do you really want to edit the LineString layer ?"))
            self.__confDlg.rejected.connect(self.__checkZeros)
            self.__confDlg.okButton().clicked.connect(self.__onConfirmLine)
            self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel)
            self.__confDlg.show()
        elif origin != 0:
            situations = self.__msgDlg.getSituations()
            case = True
            for s in situations:
                layer = self.__layers[s['layer'] - 1]
                if not layer.isEditable():
                    case = False
                    break
            if not case:
                self.__confDlg.setMessage(
                    QCoreApplication.translate("VDLTools", "Do you really want to edit the Point layer(s) ?"))
                self.__confDlg.rejected.connect(self.__checkZeros)
                self.__confDlg.okButton().clicked.connect(self.__onConfirmPoints)
                self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel)
                self.__confDlg.show()
            else:
                self.__confirmPoints()
        else:
            self.__confirmLine()

    def __getOtherLayers(self):
        """
        To get all points layers that can be used
        :return: layers list
        """
        layerList = []
        types = [QgsWKBTypes.PointZ, QgsWKBTypes.LineStringZ, QgsWKBTypes.CircularStringZ, QgsWKBTypes.CompoundCurveZ,
                 QgsWKBTypes.CurvePolygonZ, QgsWKBTypes.PolygonZ]
        for layer in self.canvas().layers():
            if layer.type() == QgsMapLayer.VectorLayer and QGis.fromOldWkbType(layer.wkbType()) in types:
                layerList.append(layer)
        return layerList

    def __onMsgPass(self):
        """
        When the Pass button in Profile Message Dialog is pushed
        """
        self.__msgDlg.reject()

    def __onConfirmCancel(self):
        """
        When the Cancel button in Profile Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __onMsgLine(self):
        """
        When the Line button in Profile Message Dialog is pushed
        """
        self.__setConfirmDialog(0)

    def __onMsgPoints(self):
        """
        When the Points button in Profile Message Dialog is pushed
        """
        self.__setConfirmDialog(1)

    def __onConfirmLine(self):
        """
        When the Line button in Profile Confirm Dialog is pushed
        """
        self.__confDlg.accept()
        self.__confirmLine()

    def __checkZeros(self):
        """
        To check if there are zeros in selected objects
        """
        alts = []
        nb_not_none = []
        for i in range(len(self.__points)):
            zz = self.__points[i]['z']
            alt = 0
            nb = 0
            for z in zz:
                if z is not None:
                    nb += 1
                    if z > alt:
                        alt = z
            alts.append(alt)
            nb_not_none.append(nb)

        zeros = []
        for i in range(len(self.__points)):
            if alts[i] == 0:
                if i == 0:
                    ap = None
                    app = None
                    j = 1
                    while True:
                        if i+j > len(self.__points)-1:
                            break
                        if alts[i+j] != 0:
                            ap = j
                            j += 1
                            while True:
                                if i+j > len(self.__points)-1:
                                    break
                                if alts[i+j] != 0:
                                    app = j
                                    break
                                j += 1
                            break
                        j += 1
                    if ap is None or app is None:
                        zeros.append([i, None, None])
                    else:
                        big_d = Finder.sqrDistForCoords(self.__points[ap]['x'], self.__points[app]['x'],
                                                        self.__points[ap]['y'], self.__points[app]['y'])
                        small_d = Finder.sqrDistForCoords(self.__points[i]['x'], self.__points[ap]['x'],
                                                          self.__points[i]['y'], self.__points[ap]['y'])
                        if small_d < (old_div(big_d, 4)):
                            zextra = alts[app] + (1 + old_div(small_d, big_d)) * (alts[ap] - alts[app])
                            zeros.append([i, zextra, nb_not_none[i]])
                        else:
                            zeros.append([i, None, None])
                elif i == len(self.__points)-1:
                    av = None
                    avv = None
                    j = 1
                    while True:
                        if i-j < 0:
                            break
                        if alts[i-j] != 0:
                            av = j
                            j += 1
                            while True:
                                if i-j < 0:
                                    break
                                if alts[i-j] != 0:
                                    avv = j
                                    break
                                j += 1
                            break
                        j += 1
                    if av is None or avv is None:
                        zeros.append([i, None, None])
                    else:
                        big_d = Finder.sqrDistForCoords(self.__points[i-av]['x'], self.__points[i-avv]['x'],
                                                        self.__points[i-av]['y'], self.__points[i-avv]['y'])
                        small_d = Finder.sqrDistForCoords(self.__points[i]['x'], self.__points[i-av]['x'],
                                                          self.__points[i]['y'], self.__points[i-av]['y'])
                        if small_d < (old_div(big_d, 4)):
                            zextra = alts[i-avv] + (1 + old_div(small_d, big_d)) * (alts[i-av] - alts[i-avv])
                            zeros.append([i, zextra, nb_not_none[i]])
                        else:
                            zeros.append([i, None, None])
                else:
                    av = None
                    j = 1
                    while True:
                        if i-j < 0:
                            break
                        if alts[i-j] != 0:
                            av = j
                            break
                        j += 1
                    ap = None
                    j = 1
                    while True:
                        if i+j > len(self.__points)-1:
                            break
                        if alts[i+j] != 0:
                            ap = j
                            break
                        j += 1
                    if av is None or ap is None:
                        zeros.append([i, None, None])
                    else:
                        d0 = Finder.sqrDistForCoords(
                            self.__points[i-av]['x'], self.__points[i]['x'], self.__points[i-av]['y'],
                            self.__points[i]['y'])
                        d1 = Finder.sqrDistForCoords(
                            self.__points[i+ap]['x'], self.__points[i]['x'], self.__points[i+ap]['y'],
                            self.__points[i]['y'])
                        zinter = old_div((d0*alts[i+ap] + d1*alts[i-av]), (d0 + d1))
                        zeros.append([i, zinter, nb_not_none[i]])
        if len(zeros) > 0:
            self.__zeroDlg = ProfileZerosDialog(zeros)
            self.__zeroDlg.rejected.connect(self.__cancel)
            self.__zeroDlg.passButton().clicked.connect(self.__onZeroPass)
            self.__zeroDlg.applyButton().clicked.connect(self.__onZeroApply)
            self.__zeroDlg.show()
        else:
            self.__cancel()

    def __onZeroPass(self):
        """
        When the Pass button in Profile Zeros Dialog is pushed
        """
        self.__zeroDlg.reject()

    def __onZeroApply(self):
        """
        When the Apply button in Profile Zeros Dialog is pushed
        """
        self.__zeroDlg.accept()
        zeros = self.__zeroDlg.getZeros()

        num_lines = len(self.__selectedIds)
        lines = []
        for iden in self.__selectedIds:
            for f in self.__lineLayer.selectedFeatures():
                if f.id() == iden:
                    line, curved = GeometryV2.asLineV2(f.geometry(), self.__iface)
                    lines.append(line)
                    break
        for z in zeros:
            for i in range(num_lines):
                if self.__points[z[0]]['z'][i] is not None:
                    index = z[0]-self.__selectedStarts[i]
                    if not self.__selectedDirections[i]:
                        index = lines[i].numPoints()-1-index
                    lines[i].setZAt(index, z[1])
            if z[2] > 1:
                zz = self.__points[z[0]]['z']
                for p in range(len(zz)-num_lines):
                    if zz[num_lines+p] is not None:
                        feat = self.__features[z[0]][p]
                        layer = self.__layers[p]
                        self.__changePoint(layer, z[0], feat, z[1])
        if not self.__lineLayer.isEditable():
            self.__lineLayer.startEditing()
        for i in range(len(lines)):
            geom = QgsGeometry(lines[i].clone())
            self.__lineLayer.changeGeometry(self.__selectedIds[i], geom)
            # self.__lineLayer.updateExtents()
        self.__dockWdg.clearData()
        self.__lineVertices()
        self.__createProfile()
        self.__cancel()

    def __confirmLine(self):
        """
        To change the elevations of some vertices of the line
        """
        situations = self.__msgDlg.getSituations()
        num_lines = len(self.__selectedIds)
        points = {}
        for s in situations:
            if s['point'] not in points:
                points[s['point']] = self.__points[s['point']]['z'][s['layer']+num_lines-1]
            else:
                diff = abs(self.__points[s['point']]['z'][s['layer']+num_lines-1] - points[s['point']])
                if diff > 0.001:
                    QMessageBox.information(
                        None, QCoreApplication.translate("VDLTools", "Elevation"),
                        QCoreApplication.translate("VDLTools", "There is more than one elevation for the point ") +
                        str(s['point'])
                    )
                    return
        self.__msgDlg.accept()
        lines = []
        for iden in self.__selectedIds:
            for f in self.__lineLayer.selectedFeatures():
                if f.id() == iden:
                    line, curved = GeometryV2.asLineV2(f.geometry(), self.__iface)
                    lines.append(line)
                    break
        for s in situations:
            z = self.__points[s['point']]['z'][s['layer']+num_lines-1]
            for i in range(num_lines):
                if self.__points[s['point']]['z'][i] is not None:
                    index = s['point']-self.__selectedStarts[i]
                    if not self.__selectedDirections[i]:
                        index = lines[i].numPoints()-1-index
                    lines[i].setZAt(index, z)
        if not self.__lineLayer.isEditable():
            self.__lineLayer.startEditing()
        for i in range(len(lines)):
            geom = QgsGeometry(lines[i].clone())
            self.__lineLayer.changeGeometry(self.__selectedIds[i], geom)
        self.__dockWdg.clearData()
        self.__lineVertices()
        self.__createProfile()
        self.__checkZeros()

    def __onConfirmPoints(self):
        """
        When the Points button in Profile Confirm Dialog is pushed
        """
        self.__confDlg.accept()
        self.__confirmPoints()

    def __confirmPoints(self):
        """
        To change the elevations of certain points
        """
        self.__msgDlg.accept()
        situations = self.__msgDlg.getSituations()
        num_lines = len(self.__selectedIds)
        for s in situations:
            layer = self.__layers[s['layer']-1]
            feat = self.__features[s['point']][s['layer']-1]
            newZ = 0
            for i in range(num_lines):
                if self.__points[s['point']]['z'][i] is not None:
                    newZ = self.__points[s['point']]['z'][i]
                    break
            self.__changePoint(layer, s['point'], feat, newZ)
        self.__dockWdg.clearData()
        self.__lineVertices()
        self.__createProfile()
        self.__checkZeros()

    def __changePoint(self, layer, pos, feat, newZ):
        """
        To change Vertex elevation
        :param layer: layer containing the object
        :param pos: vertex position in the object (if not a point)
        :param feat: QgsFeature of the object
        :param newZ: new elevation
        """
        if layer.geometryType() == QGis.Polygon:
            closest = feat.geometry().closestVertex(
                QgsPoint(self.__points[pos]['x'], self.__points[pos]['y']))
            feat_v2, curved = GeometryV2.asPolygonV2(feat.geometry(), self.__iface)
            position = GeometryV2.polygonVertexId(feat_v2, closest[1])
            vertex = feat_v2.vertexAt(position)
            feat_v2.deleteVertex(position)
            vertex.setZ(newZ)
            feat_v2.insertVertex(position, vertex)
        elif layer.geometryType() == QGis.Line:
            closest = feat.geometry().closestVertex(
                QgsPoint(self.__points[pos]['x'], self.__points[pos]['y']))
            feat_v2, curved = GeometryV2.asLineV2(feat.geometry(), self.__iface)
            feat_v2.setZAt(closest[1], newZ)
        else:
            feat_v2 = GeometryV2.asPointV2(feat.geometry(), self.__iface)
            feat_v2.setZ(newZ)
        if not layer.isEditable():
            layer.startEditing()
        layer.changeGeometry(feat.id(), QgsGeometry(feat_v2))

    def __onLayCancel(self):
        """
        When the Cancel button in Profile Layers Dialog is pushed
        """
        self.__layDlg.reject()
        self.__isChoosed = False

    def __lineVertices(self, checkLayers=False):
        """
        To check if vertices of others layers are crossing the displaying line
        :param checkLayers: if we want to get the list of the other layers in return
        :return: other layers list if requested
        """
        if checkLayers:
            availableLayers = self.__getOtherLayers()
            otherLayers = []
        self.__points = []
        self.__selectedStarts = []
        num = 0
        num_lines = len(self.__selectedIds)
        for iden in self.__selectedIds:
            self.__selectedStarts.append(max(0, len(self.__points)-1))
            direction = self.__selectedDirections[num]
            selected = None
            for f in self.__lineLayer.selectedFeatures():
                if f.id() == iden:
                    selected = f
                    break
            if selected is None:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "Error on selected"), level=QgsMessageBar.CRITICAL,
                    duration=0
                )
                continue
            line_v2, curved = GeometryV2.asLineV2(selected.geometry(), self.__iface)
            if direction:
                rg = range(line_v2.numPoints())
            else:
                rg = range(line_v2.numPoints()-1, -1, -1)
            rg_positions = []
            for i in rg:
                pt_v2 = line_v2.pointN(i)
                x = pt_v2.x()
                y = pt_v2.y()
                doublon = False
                for position in rg_positions:
                    if position['x'] == x and position['y'] == y and position['iden'] == iden:
                        self.__iface.messageBar().pushMessage(
                           QCoreApplication.translate("VDLTools", "Beware! the line ") + str(iden) +
                           QCoreApplication.translate("VDLTools", " has 2 identical summits on the vertex ") +
                           str(i-1) + QCoreApplication.translate("VDLTools", " same coordinates (X and Y). "
                                                                             "Please correct the line geometry."),
                           level=QgsMessageBar.CRITICAL, duration=0
                        )
                        doublon = True
                        break
                for item in self.__points:
                    if item['x'] == x and item['y'] == y:
                        item['z'][num] = pt_v2.z()
                        rg_positions.append({'x': x, 'y': y, 'iden': iden})
                        doublon = True
                        break
                if not doublon:
                    rg_positions.append({'x': x, 'y': y, 'iden': iden})
                    z = []
                    for j in range(num_lines):
                        if j == num:
                            if pt_v2.z() == pt_v2.z():
                                z.append(pt_v2.z())
                            else:
                                z.append(0)
                        else:
                            z.append(None)
                    self.__points.append({'x': x, 'y': y, 'z': z})
                    if checkLayers:
                        for layer in availableLayers:
                            if layer in otherLayers:
                                continue
                            laySettings = QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.Vertex, self.SEARCH_TOLERANCE,
                                                                       QgsTolerance.LayerUnits)
                            f_l = Finder.findClosestFeatureAt(self.toMapCoordinates(layer, QgsPoint(x, y)),
                                                              self.canvas(), [laySettings])

                            if f_l is not None:
                                if layer == self.__lineLayer:
                                    other = False
                                    if f_l[0].id() not in self.__selectedIds:
                                        other = True
                                    else:
                                        fs = Finder.findFeaturesAt(QgsPoint(x, y), laySettings, self)
                                        for f in fs:
                                            if f.id() not in self.__selectedIds:
                                                vertex = f.geometry().closestVertex(QgsPoint(x, y))
                                                if vertex[4] < self.SEARCH_TOLERANCE:
                                                    other = True
                                                    break
                                    if other and layer not in otherLayers:
                                        otherLayers.append(layer)
                                elif layer not in otherLayers:
                                    otherLayers.append(layer)
            num += 1
        if checkLayers:
            return otherLayers

    def __onLayOk(self):
        """
        When the Ok button in Profile Layers Dialog is pushed
        """
        self.__layDlg.accept()
        self.__layers = self.__layDlg.getLayers()
        self.__usedMnts = self.__layDlg.getUsedMnts()
        self.__layOk()

    def __layOk(self):
        """
        To create the profile
        """
        self.__createProfile()
        self.__checkSituations()
        self.__isChoosed = False

    def __createProfile(self):
        """
        Create the profile in the dock
        """
        self.__features = []

        for points in self.__points:
            feat = []
            x = points['x']
            y = points['y']
            z = points['z']
            for layer in self.__layers:
                laySettings = QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.Vertex, self.SEARCH_TOLERANCE,
                                                           QgsTolerance.LayerUnits)
                f_l = Finder.findClosestFeatureAt(self.toMapCoordinates(layer, QgsPoint(x, y)), self.canvas(),
                                                  [laySettings])
                if f_l is None:
                    feat.append(None)
                    z.append(None)
                else:
                    if f_l[1].geometryType() == QGis.Polygon:
                        closest = f_l[0].geometry().closestVertex(QgsPoint(x, y))
                        polygon_v2, curved = GeometryV2.asPolygonV2(f_l[0].geometry(), self.__iface)
                        zp = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, closest[1])).z()
                        feat.append(f_l[0])
                        if zp is None or zp != zp:
                            z.append(0)
                        else:
                            z.append(zp)
                    elif f_l[1].geometryType() == QGis.Line:
                        f_ok = None
                        if layer == self.__lineLayer:
                            if f_l[0].id() not in self.__selectedIds:
                                f_ok = f_l[0]
                            else:
                                fs = Finder.findFeaturesAt(QgsPoint(x, y), laySettings, self)
                                for f in fs:
                                    if f.id() not in self.__selectedIds:
                                        vertex = f.geometry().closestVertex(QgsPoint(x, y))
                                        if vertex[4] < self.SEARCH_TOLERANCE:
                                            f_ok = f
                                            break
                        else:
                            f_ok = f_l[0]
                        if f_ok is not None:
                            closest = f_ok.geometry().closestVertex(QgsPoint(x, y))
                            feat.append(f_ok)
                            line, curved = GeometryV2.asLineV2(f_ok.geometry(), self.__iface)
                            zp = line.zAt(closest[1])
                            if zp is None or zp != zp:
                                z.append(0)
                            else:
                                z.append(zp)
                        else:
                            feat.append(None)
                            z.append(None)
                    else:
                        zp = GeometryV2.asPointV2(f_l[0].geometry(), self.__iface).z()
                        feat.append(f_l[0])
                        if zp is None or zp != zp:
                            z.append(0)
                        else:
                            z.append(zp)
            self.__features.append(feat)
        self.__calculateProfile()

    def __getNames(self):
        """
        To get the names from connected lines layers
        :return: the names list
        """
        names = [self.__lineLayer.name()]
        for layer in self.__layers:
            if layer.name() == self.__lineLayer.name():
                names.append(layer.name() + " conn.")
            else:
                names.append(layer.name())
        return names

    @staticmethod
    def __contains(line, point):
        """
        To check if a position is a line vertex
        :param line: the line
        :param point: the position
        :return: the vertex id in the line, or -1
        """
        pos = 0
        if point is None:
            return -1
        for pt in line:
            if pt.x() == point.x() and pt.y() == point.y():
                return pos
            pos += 1
        return -1

    def keyReleaseEvent(self, event):
        """
        When keyboard is pressed
        :param event: keyboard event
        """
        if event.key() == Qt.Key_Escape:
            self.__cancel()

    def canvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """
        if not self.__isChoosed:
            if self.__lineLayer is not None:
                laySettings = QgsSnappingUtils.LayerConfig(self.__lineLayer, QgsPointLocator.All, 10,
                                                           QgsTolerance.Pixels)
                f_l = Finder.findClosestFeatureAt(event.mapPoint(), self.canvas(), [laySettings])
                if not self.__inSelection:
                    if f_l is not None and self.__lastFeatureId != f_l[0].id():
                        self.__lastFeature = f_l[0]
                        self.__lastFeatureId = f_l[0].id()
                        self.__lineLayer.setSelectedFeatures([f_l[0].id()])
                    if f_l is None:
                        self.__cancel()
                else:
                    if f_l is not None and (self.__selectedIds is None or f_l[0].id() not in self.__selectedIds):
                        line = f_l[0].geometry().asPolyline()
                        if self.__contains(line, self.__endVertex) > -1:
                            self.__lastFeature = f_l[0]
                            self.__lastFeatureId = f_l[0].id()
                            features = self.__selectedIds + [f_l[0].id()]
                            self.__lineLayer.setSelectedFeatures(features)

                        elif self.__contains(line, self.__startVertex) > -1:
                            self.__lastFeature = f_l[0]
                            self.__lastFeatureId = f_l[0].id()
                            features = self.__selectedIds + [f_l[0].id()]
                            self.__lineLayer.setSelectedFeatures(features)

                        else:
                            self.__lineLayer.setSelectedFeatures(self.__selectedIds)
                            self.__lastFeatureId = None
                            self.__lastFeature = None

                if f_l is None:
                    if self.__selectedIds is not None:
                        self.__lineLayer.setSelectedFeatures(self.__selectedIds)
                    self.__lastFeatureId = None
                    self.__lastFeature = None

    def canvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        self.__rubberSit.reset()
        self.__rubberDif.reset()
        if event.button() == Qt.RightButton:
            if self.__lineLayer.selectedFeatures() is not None and self.__selectedIds is not None:
                self.__isChoosed = True
                self.__setLayerDialog()
        elif event.button() == Qt.LeftButton:
            if self.__lastFeature is not None and \
                    (self.__selectedIds is None or self.__lastFeature.id() not in self.__selectedIds):
                self.__inSelection = True
                line = self.__lastFeature.geometry().asPolyline()
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools",
                                               "Select more lines with click left or process "
                                               "with click right (ESC to undo)"),
                    level=QgsMessageBar.INFO, duration=3)
                if self.__selectedIds is None:
                    self.__selectedIds = []
                    self.__startVertex = line[0]
                    self.__endVertex = line[-1]
                    self.__selectedDirections = []
                    self.__selectedDirections.append(True)  # direction du premier prime
                    self.__selectedIds.append(self.__lastFeatureId)
                else:
                    pos = self.__contains(line, self.__startVertex)
                    if pos > -1:
                        self.__selectedIds = [self.__lastFeatureId] + self.__selectedIds
                        if pos == 0:
                            direction = False
                            self.__startVertex = line[-1]
                        else:
                            direction = True
                            self.__startVertex = line[0]
                        self.__selectedDirections = [direction] + self.__selectedDirections
                    else:
                        pos = self.__contains(line, self.__endVertex)
                        self.__selectedIds.append(self.__lastFeatureId)
                        if pos == 0:
                            direction = True
                            self.__endVertex = line[-1]
                        else:
                            direction = False
                            self.__endVertex = line[0]
                        self.__selectedDirections.append(direction)
                    self.__lineLayer.setSelectedFeatures(self.__selectedIds)

    def __calculateProfile(self):
        """
        To calculate the profile and display it
        """
        if self.__points is None:
            return
        self.__dockWdg.clearData()
        if len(self.__points) == 0:
            return
        self.__dockWdg.setProfiles(self.__points, len(self.__selectedIds))
        self.__dockWdg.attachCurves(self.__getNames(), self.ownSettings, self.__usedMnts)

    def __checkSituations(self):
        """
        To check if point with no elevation on line, and one or more elevation from other layers,
        and if there are different elevations at the same point
        """
        situations = []
        differences = []
        for p in range(len(self.__points)):
            pt = self.__points[p]
            num_lines = len(self.__selectedIds)
            zz = []
            for i in range(num_lines):
                if pt['z'][i] is not None:
                    zz.append(i)
            if len(zz) == 0:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "No line z ?!?"), level=QgsMessageBar.WARNING)
            elif len(zz) == 1:
                z0 = pt['z'][zz[0]]
                for i in range(num_lines, len(pt['z'])):
                    if pt['z'][i] is None:
                        continue
                    if abs(pt['z'][i]-z0) > self.ALT_TOLERANCE:
                        situations.append({'point': p, 'layer': (i-num_lines+1), 'vertex': z0})
            elif len(zz) == 2:
                z0 = pt['z'][zz[0]]
                if abs(pt['z'][zz[1]] - z0) > self.ALT_TOLERANCE:
                    differences.append({'point': p, 'v1': z0, 'v2': pt['z'][zz[1]]})
                else:
                    for i in range(num_lines, len(pt['z'])):
                        if pt['z'][i] is None:
                            continue
                        if abs(pt['z'][i]-z0) > self.ALT_TOLERANCE:
                            situations.append({'point': p, 'layer': (i-num_lines+1), 'vertex': z0})
            else:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "More than 2 lines z ?!?"), level=QgsMessageBar.WARNING)

        if (len(situations) > 0) or (len(differences) > 0):
            self.__setMessageDialog(situations, differences, self.__getNames())
            self.__rubberSit.reset()
            self.__rubberDif.reset()
            for situation in situations:
                pt = self.__points[situation['point']]
                point = QgsPoint(pt['x'], pt['y'])
                if self.__rubberSit.numberOfVertices() == 0:
                    self.__rubberSit.setToGeometry(QgsGeometry().fromPoint(point), None)
                else:
                    self.__rubberSit.addPoint(point)
            for difference in differences:
                pt = self.__points[difference['point']]
                point = QgsPoint(pt['x'], pt['y'])
                if self.__rubberDif.numberOfVertices() == 0:
                    self.__rubberDif.setToGeometry(QgsGeometry().fromPoint(point), None)
                else:
                    self.__rubberDif.addPoint(point)

            self.__msgDlg.show()
        else:
            self.__checkZeros()
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)
class GeomEditorDialog(QDialog, Ui_GeomEditor, SettingDialog):
    def __init__(self, layer, feature, mapCanvas, parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.settings = MySettings()
        SettingDialog.__init__(self, self.settings, False, True)
        self.mapCanvas = mapCanvas
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.feature = feature
        self.initialGeometry = QgsGeometry(feature.geometry())
        self.layer = layer

        # close if no geom, hide "sketch current point" if not needed
        geomType = layer.geometryType()
        if not geomType in (QGis.Point, QGis.Line, QGis.Polygon):
            self.close()
            return
        if geomType == QGis.Point:
            self.pointRubberGroup.hide()

        # editors management
        self.editorLayout = QGridLayout(self.editorContainer)
        self.editor = GeomEditor(layer, feature.geometry())
        self.displayCombo.setCurrentIndex(0)
        self.displayCombo.currentIndexChanged.connect(self.setEditor)
        self.setEditor()

        # rubber bands
        self.featureRubber = QgsRubberBand(mapCanvas)
        self.currentPointRubber = QgsRubberBand(mapCanvas)
        self.settings.setting("featureRubberColor").valueChanged.connect(self.updateFeatureRubber)
        self.settings.setting("featureRubberSize").valueChanged.connect(self.updateFeatureRubber)
        self.settings.setting("currentPointRubberSize").valueChanged.connect(self.updateCurrentPointRubber)
        self.settings.setting("currentPointRubberColor").valueChanged.connect(self.updateCurrentPointRubber)
        self.settings.setting("currentPointRubberIcon").valueChanged.connect(self.updateCurrentPointRubber)
        self.updateFeatureRubber()
        self.updateCurrentPointRubber()
        self.geometryChanged()

        # GUI signals connection
        self.applyButton.clicked.connect(self.applyGeometry)
        self.resetButton.clicked.connect(self.resetGeometry)
        self.sketchGeometry.clicked.connect(self.geometryChanged)
        self.displayPointRubber.clicked.connect(self.currentPointRubber.reset)
        self.layerEditable()
        layer.editingStopped.connect(self.layerEditable)
        layer.editingStarted.connect(self.layerEditable)

        # set texts in UI
        self.layerLabel.setText(layer.name())
        try:
            featureTitle = unicode(feature[layer.displayField()])
        except KeyError:
            featureTitle = ""
        if featureTitle == "":
            featureTitle = str(feature.id())
        self.featureEdit.setText(featureTitle)

    def setEditor(self):
        self.editorLayout.removeWidget(self.editor)
        geom = self.editor.getGeom()
        idx = self.displayCombo.currentIndex()
        if idx == -99999:
            editor = CellEditor
        elif idx == 0:
            editor = WktEditor
        elif idx == 1:
            editor = WkbEditor
        else:
            self.editor = GeomEditor
            return
        self.editor = editor(self.layer, geom, self)
        self.editorLayout.addWidget(self.editor, 0, 0, 1, 1)

        self.editor.currentPointChanged.connect(self.drawCurrentPoint)
        self.editor.geometryChanged.connect(self.geometryChanged)

    def resetGeometry(self):
        self.editor.setGeom(self.initialGeometry)

    def closeEvent(self, e):
        self.featureRubber.reset()
        self.currentPointRubber.reset()
        self.layer.editingStarted.disconnect(self.layerEditable)
        self.layer.editingStopped.disconnect(self.layerEditable)
        self.editor.closeEditor()
        QDialog.closeEvent(self, e)

    def layerEditable(self):
        layerIsEditable = self.layer.isEditable()
        self.resetButton.setEnabled(layerIsEditable)
        self.applyButton.setEnabled(layerIsEditable)

    def geometryChanged(self):
        self.featureRubber.reset()
        self.currentPointRubber.reset()
        if self.editor.isGeomValid():
            self.displayCombo.setEnabled(True)
            self.applyButton.setEnabled(self.layer.isEditable())
            geomStatus = "Geometry is valid"
            if self.sketchGeometry.isChecked():
                self.featureRubber.setToGeometry(self.editor.getGeom(), self.layer)
        else:
            self.applyButton.setEnabled(False)
            self.displayCombo.setEnabled(False)
            geomStatus = "Geometry is not valid"
        self.geomStatusLabel.setText(geomStatus)

    @pyqtSlot(QgsGeometry)
    def drawCurrentPoint(self, point):
        if self.displayPointRubber.isChecked():
            self.currentPointRubber.setToGeometry(point, None)
            self.mapCanvas.refresh()

    def applyGeometry(self):
        geometry = self.editor.getGeom()
        if geometry is not None:
            self.layer.changeGeometry(self.feature.id(), geometry)
            self.layer.updateExtents()
            self.layer.setCacheImage(None)
            self.layer.triggerRepaint()
            
    def updateFeatureRubber(self):
        self.featureRubber.setColor(self.settings.value("featureRubberColor"))
        self.featureRubber.setWidth(self.settings.value("featureRubberSize"))
        self.layer.triggerRepaint()
        
    def updateCurrentPointRubber(self):
        self.currentPointRubber.setIconSize(self.settings.value("currentPointRubberSize"))
        self.currentPointRubber.setColor(self.settings.value("currentPointRubberColor"))
        self.currentPointRubber.setIcon(self.settings.value("currentPointRubberIcon"))
        self.mapCanvas.refresh()
Example #18
0
class FinderBox(QComboBox):

    running = False
    toFinish = 0

    searchStarted = pyqtSignal()
    searchFinished = pyqtSignal()

    def __init__(self, finders, iface, parent=None):
        self.iface = iface
        self.mapCanvas = iface.mapCanvas()
        self.rubber = QgsRubberBand(self.mapCanvas)
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

        QComboBox.__init__(self, parent)
        self.setEditable(True)
        self.setInsertPolicy(QComboBox.InsertAtTop)
        self.setMinimumHeight(27)
        self.setSizePolicy(QSizePolicy.Expanding,
                           QSizePolicy.Fixed)

        self.insertSeparator(0)
        self.lineEdit().returnPressed.connect(self.search)

        self.resultView = QTreeView()
        self.resultView.setHeaderHidden(True)
        self.resultView.setMinimumHeight(300)
        self.resultView.activated.connect(self.itemActivated)
        self.resultView.pressed.connect(self.itemPressed)
        self.setView(self.resultView)

        self.resultModel = ResultModel(self)
        self.setModel(self.resultModel)

        self.finders = finders
        for finder in self.finders.values():
            finder.resultFound.connect(self.resultFound)
            finder.limitReached.connect(self.limitReached)
            finder.finished.connect(self.finished)
            finder.message.connect(self.message)

    def __del__(self):
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    def search(self):
        if self.running:
            return

        toFind = self.lineEdit().text()
        if not toFind or toFind == '':
            return

        self.running = True
        self.searchStarted.emit()

        self.resultModel.clearResults()
        self.resultModel.truncateHistory(MySettings().value("historyLength"))
        self.resultModel.setLoading(True)
        self.showPopup()

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        # create categories in special order and count activated ones
        for finder in self.finders.values():
            if finder.activated():
                self.resultModel.addResult(finder.name)

        bbox = self.mapCanvas.fullExtent()
        for finder in self.finders.values():
            if finder.activated():
                finder.start(toFind, bbox=bbox)

    def stop(self):
        for finder in self.finders.values():
            if finder.isRunning():
                finder.stop()

    def resultFound(self, finder, layername, value, geometry, srid):
        self.resultModel.addResult(finder.name, layername, value, geometry, srid)
        self.resultView.expandAll()

    def limitReached(self, finder, layername):
        self.resultModel.addEllipsys(finder.name, layername)

    def finished(self, finder):
        for finder in self.finders.values():
            if finder.isRunning():
                return

        self.running = False
        self.searchFinished.emit()

        self.resultModel.setLoading(False)

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

    def message(self, finder, message, level):
        self.iface.messageBar().pushMessage("Quick Finder", message, level, 3)

    def itemActivated(self, index):
        item = self.resultModel.itemFromIndex(index)
        self.showItem(item)

    def itemPressed(self, index):
        item = self.resultModel.itemFromIndex(index)
        if QApplication.mouseButtons() == Qt.LeftButton:
            self.showItem(item)

    def showItem(self, item):
        if isinstance(item, ResultItem):
            self.resultModel.setSelected(item, self.resultView.palette())
            geometry = self.transformGeom(item)
            self.rubber.reset(geometry.type())
            self.rubber.setToGeometry(geometry, None)
            self.zoomToRubberBand()
            return

        if isinstance(item, GroupItem):
            child = item.child(0)
            if isinstance(child, ResultItem):
                self.resultModel.setSelected(item, self.resultView.palette())
                self.rubber.reset(child.geometry.type())
                for i in xrange(0, item.rowCount()):
                    geometry = self.transformGeom(item.child(i))
                    self.rubber.addGeometry(geometry, None)
                self.zoomToRubberBand()
            return

        if item.__class__.__name__ == 'QStandardItem':
            self.resultModel.setSelected(None, self.resultView.palette())
            self.rubber.reset()
            return

    def transformGeom(self, item):
        geometry = item.geometry
        src_crs = QgsCoordinateReferenceSystem()
        src_crs.createFromSrid(item.srid)
        dest_crs = self.mapCanvas.mapRenderer().destinationCrs()
        geom = item.geometry
        geom.transform( QgsCoordinateTransform(src_crs, dest_crs) )
        return geom

    def zoomToRubberBand(self):
        geom = self.rubber.asGeometry()
        if geom:
            rect = geom.boundingBox()
            rect.scale(1.5)
            self.mapCanvas.setExtent(rect)
            self.mapCanvas.refresh()
Example #19
0
class IntersectTool(QgsMapTool):
    """
    Map tool class to create temporary circle, with center point
    """

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/intersect_icon.png'
        self.text = QCoreApplication.translate("VDLTools", "From intersection")
        self.setCursor(Qt.ArrowCursor)
        self.__lineLayerID = None
        self.__pointLayerID = None
        self.__rubber = None
        self.ownSettings = None
        self.__isEditing = False
        self.__distance = 0

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__rubber = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setIconSize(20)
        self.__rubber.setWidth(2)
        self.__distance = 6.0

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__cancel()
        self.__rubber = None
        QgsMapTool.deactivate(self)

    def __cancel(self):
        """
        To cancel used variables
        """
        if self.__rubber is not None:
            self.__rubber.reset()
        self.__isEditing = False

    def __setDistanceDialog(self, mapPoint):
        """
        To create an Intersect Distance Dialog
        :param mapPoint: radius of the circle
        """
        self.__dstDlg = IntersectDistanceDialog(mapPoint)
        self.__dstDlg.rejected.connect(self.__cancel)
        self.__dstDlg.okButton().clicked.connect(self.__onDstOk)
        self.__dstDlg.cancelButton().clicked.connect(self.__onDstCancel)
        self.__dstDlg.observation().setValue(self.__distance)
        self.__dstDlg.observation().selectAll()
        self.__dstDlg.show()

    def __onDstOk(self):
        """
        When the Ok button in Intersect Distance Dialog is pushed
        """
        self.__distance = float(self.__dstDlg.observation().text())
        circle = QgsCircularStringV2()
        x = self.__dstDlg.mapPoint().x()
        y = self.__dstDlg.mapPoint().y()
        circle.setPoints([QgsPointV2(x + self.__distance * cos(pi / 180 * a), y + self.__distance * sin(pi / 180 * a))
                          for a in range(0, 361, 90)])
        lineLayer = self.__lineLayer()
        lineLayer.startEditing()
        feature = QgsFeature()
        feature.setGeometry(QgsGeometry(circle))
        fields = lineLayer.pendingFields()
        feature.setFields(fields)
        fieldsNames = [fields.at(pos).name() for pos in range(fields.count())]
        if "distance" in fieldsNames:
            feature.setAttribute("distance", self.__distance)
        if "x" in fieldsNames:
            feature.setAttribute("x", self.__dstDlg.mapPoint().x())
        if "y" in fieldsNames:
            feature.setAttribute("y", self.__dstDlg.mapPoint().y())
        lineLayer.addFeature(feature)
        # lineLayer.updateExtents()
        lineLayer.commitChanges()

        # center
        pointLayer = self.__pointLayer()
        pointLayer.startEditing()
        feature = QgsFeature()
        feature.setGeometry(QgsGeometry().fromPoint(self.__dstDlg.mapPoint()))
        fields = pointLayer.pendingFields()
        feature.setFields(fields)
        pointLayer.addFeature(feature)
        pointLayer.commitChanges()

        self.__dstDlg.accept()
        self.__cancel()

    def __onDstCancel(self):
        """
        When the Cancel button in Intersect Distance Dialog is pushed
        """
        self.__dstDlg.reject()

    def canvasMoveEvent(self, mouseEvent):
        """
        When the mouse is moved
        :param mouseEvent: mouse event
        """
        if not self.__isEditing:
            self.__rubber.reset()
            match = Finder.snap(mouseEvent.mapPoint(), self.canvas())
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer():
                        self.__rubber.setIcon(4)
                    else:
                        self.__rubber.setIcon(1)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(point, self.canvas(), self)
                    if intersection is not None:
                        self.__rubber.setIcon(1)
                        point = intersection
                    else:
                        self.__rubber.setIcon(3)
                self.__rubber.setToGeometry(QgsGeometry().fromPoint(point), None)

    def canvasReleaseEvent(self, mouseEvent):
        """
        When the mouse is clicked
        :param mouseEvent: mouse event
        """
        if mouseEvent.button() != Qt.LeftButton:
            return
        match = Finder.snap(mouseEvent.mapPoint(), self.canvas())
        if match.hasVertex() or match.hasEdge():
            point = match.point()
            if match.hasEdge():
                intersection = Finder.snapCurvedIntersections(match.point(), self.canvas(), self)
                if intersection is not None:
                    point = intersection
            self.__isEditing = True
            self.__setDistanceDialog(point)

    def __lineLayer(self):
        """
        To get the line layer to create the circle
        :return: a line layer
        """
        if self.ownSettings is not None:
            if self.ownSettings.linesLayer is not None:
                layer = self.ownSettings.linesLayer
                self.__lineLayerID = layer.id()
                return layer
        layer = QgsMapLayerRegistry.instance().mapLayer(self.__lineLayerID)
        if layer is None:
            epsg = self.canvas().mapRenderer().destinationCrs().authid()
            layer = QgsVectorLayer("LineString?crs=%s&index=yes&field=distance:double&field=x:double&field=y:double"
                                   % epsg, "Memory Lines", "memory")
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            layer.layerDeleted.connect(self.__lineLayerDeleted)
            self.__lineLayerID = layer.id()
            if self.ownSettings is not None:
                self.ownSettings.linesLayer = layer
        else:
            self.__iface.legendInterface().setLayerVisible(layer, True)
        return layer

    def __lineLayerDeleted(self):
        """
        To deselect the line layer when it is deleted
        """
        self.lineLayerID = None

    def __pointLayer(self):
        """
        To get the point layer to create the center
        :return: a point layer
        """
        if self.ownSettings is not None:
            if self.ownSettings.pointsLayer is not None:
                layer = self.ownSettings.pointsLayer
                self.__pointLayerID = layer.id()
                return layer
        layer = QgsMapLayerRegistry.instance().mapLayer(self.__pointLayerID)
        if layer is None:
            epsg = self.canvas().mapRenderer().destinationCrs().authid()
            layer = QgsVectorLayer("Point?crs=%s&index=yes" % epsg, "Memory Points", "memory")
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            layer.layerDeleted.connect(self.__pointLayerDeleted)
            self.__pointLayerID = layer.id()
            if self.ownSettings is not None:
                self.ownSettings.pointsLayer = layer
        else:
            self.__iface.legendInterface().setLayerVisible(layer, True)
        return layer

    def __pointLayerDeleted(self):
        """
        To deselect the point layer when it is deleted
        """
        self.__pointLayerID = None
Example #20
0
class MoveTool(QgsMapTool):
    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.__canvas = iface.mapCanvas()
        self.__icon_path = ':/plugins/VDLTools/icons/move_icon.png'
        self.__text = QCoreApplication.translate("VDLTools",
                                                 "Move/Copy a feature")
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = 0
        self.__findVertex = 0
        self.__onMove = 0
        self.__counter = 0
        self.__layer = None
        self.__confDlg = None
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__rubberBand = None
        self.__rubberSnap = None
        self.__newFeature = None
        self.__selectedVertex = None
        self.__layerConfig = None

    def icon_path(self):
        """
        To get the icon path
        :return: icon path
        """
        return self.__icon_path

    def text(self):
        """
        To get the menu text
        :return: menu text
        """
        return self.__text

    def toolName(self):
        """
        To get the tool name
        :return: tool name
        """
        return QCoreApplication.translate("VDLTools", "Move/Copy")

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__updateList()
        self.__canvas.layersChanged.connect(self.__updateList)
        QgsProject.instance().snapSettingsChanged.connect(self.__updateList)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__canvas.layersChanged.disconnect(self.__updateList)
        QgsProject.instance().snapSettingsChanged.disconnect(self.__updateList)
        QgsMapTool.deactivate(self)

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        self.__layer.editingStarted.disconnect(self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        self.__layer.editingStopped.disconnect(self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.__canvas.mapTool == self:
            self.__iface.actionPan().trigger()

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.__canvas.setMapTool(self)

    def removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                self.__layer.editingStopped.disconnect(self.stopEditing)
            else:
                self.__layer.editingStarted.disconnect(self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None\
                and isinstance(layer, QgsVectorLayer):
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    self.__layer.editingStopped.disconnect(self.stopEditing)
                else:
                    self.__layer.editingStarted.disconnect(self.startEditing)
            self.__layer = layer
            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__updateList()
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.__canvas.mapTool == self:
                    self.__iface.actionPan().trigger()
            return
        self.action().setEnabled(False)
        self.removeLayer()

    def __pointPreview(self, point):
        """
        To create a point geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry())
        self.__newFeature = QgsPointV2(point.x(), point.y())
        self.__newFeature.addZValue(point_v2.z())
        self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Point)
        self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()),
                                        None)

    def __linePreview(self, point):
        """
        To create a line geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        line_v2, curved = GeometryV2.asLineV2(
            self.__selectedFeature.geometry())
        print(self.__selectedVertex)
        vertex = QgsPointV2()
        line_v2.pointAt(self.__selectedVertex, vertex)
        self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line)
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        if isinstance(curved, (list, tuple)):
            self.__newFeature = QgsCompoundCurveV2()
            for pos in xrange(line_v2.nCurves()):
                curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos),
                                           dx, dy)
                self.__newFeature.addCurve(curve_v2)
                if pos == 0:
                    self.__rubberBand.setToGeometry(
                        QgsGeometry(curve_v2.curveToLine()), None)
                else:
                    self.__rubberBand.addGeometry(
                        QgsGeometry(curve_v2.curveToLine()), None)
        else:
            self.__newFeature = self.__newCurve(curved, line_v2, dx, dy)
            self.__rubberBand.setToGeometry(
                QgsGeometry(self.__newFeature.curveToLine()), None)

    def __newCurve(self, curved, line_v2, dx, dy):
        if curved:
            newCurve = QgsCircularStringV2()
        else:
            newCurve = QgsLineStringV2()
        points = []
        for pos in xrange(line_v2.numPoints()):
            x = line_v2.pointN(pos).x() - dx
            y = line_v2.pointN(pos).y() - dy
            pt = QgsPointV2(x, y)
            pt.addZValue(line_v2.pointN(pos).z())
            points.append(pt)
        newCurve.setPoints(points)
        return newCurve

    def __polygonPreview(self, point):
        """
        To create a polygon geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        polygon_v2, curved = GeometryV2.asPolygonV2(
            self.__selectedFeature.geometry())
        vertex = polygon_v2.vertexAt(self.__polygonVertexId(polygon_v2))
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        self.__newFeature = QgsCurvePolygonV2()
        self.__rubberBand = QgsRubberBand(self.__canvas, QGis.Line)
        line_v2 = self.__newLine(polygon_v2.exteriorRing(), dx, dy, curved[0])
        self.__newFeature.setExteriorRing(line_v2)
        self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()),
                                        None)
        for num in xrange(polygon_v2.numInteriorRings()):
            line_v2 = self.__newLine(polygon_v2.interiorRing(num), dx, dy,
                                     curved[num + 1])
            self.__newFeature.addInteriorRing(line_v2)
            self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()),
                                          None)

    def __polygonVertexId(self, polygon_v2):
        """
        To get the id of the selected vertex from a polygon
        :param polygon_v2: the polygon as polygonV2
        :return: id as QgsVertexId
        """
        eR = polygon_v2.exteriorRing()
        if self.__selectedVertex < eR.numPoints():
            return QgsVertexId(0, 0, self.__selectedVertex, 1)
        else:
            sel = self.__selectedVertex - eR.numPoints()
            for num in xrange(polygon_v2.numInteriorRings()):
                iR = polygon_v2.interiorRing(num)
                if sel < iR.numPoints():
                    return QgsVertexId(0, num + 1, sel, 1)
                sel -= iR.numPoints()

    def __newLine(self, curve_v2, dx, dy, curved):
        """
        To create a new moved line for a part of a polygon
        :param curve_v2: the original line
        :param dx: x translation
        :param dy: y translation
        :return: the line as lineV2
        """
        if curved:
            new_line_v2 = QgsCircularStringV2()
        else:
            new_line_v2 = QgsLineStringV2()
        points = []

        for pos in xrange(curve_v2.numPoints()):
            x = curve_v2.pointN(pos).x() - dx
            y = curve_v2.pointN(pos).y() - dy
            pt = QgsPointV2(x, y)
            pt.addZValue(curve_v2.pointN(pos).z())
            points.append(pt)
        new_line_v2.setPoints(points)
        return new_line_v2

    def __onConfirmClose(self):
        """
        When the Cancel button in Move Confirm Dialog is pushed
        """
        self.__confDlg.close()
        self.__rubberBand.reset()
        self.__rubberSnap.reset()
        self.__isEditing = 0
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__rubberBand = None
        self.__rubberSnap = None
        self.__newFeature = None
        self.__selectedVertex = None
        self.__layer.removeSelection()

    def __onConfirmMove(self):
        """
        When the Move button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Error"),
                QCoreApplication.translate("VDLTools",
                                           "Geos geometry problem"),
                level=QgsMessageBar.CRITICAL)
        self.__layer.changeGeometry(self.__selectedFeature.id(), geometry)
        self.__layer.updateExtents()
        self.__onConfirmClose()

    def __onConfirmCopy(self):
        """
        When the Copy button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Error"),
                QCoreApplication.translate("VDLTools",
                                           "Geos geometry problem"),
                level=QgsMessageBar.CRITICAL)
        feature = QgsFeature(self.__layer.pendingFields())
        feature.setGeometry(geometry)
        primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn()
        for field in self.__selectedFeature.fields():
            if field.name() != primaryKey:
                feature.setAttribute(
                    field.name(),
                    self.__selectedFeature.attribute(field.name()))
        if len(self.__selectedFeature.fields()) > 0 and \
                        self.__layer.editFormConfig().suppress() != QgsEditFormConfig.SuppressOn:
            self.__iface.openFeatureForm(self.__layer, feature)
        else:
            self.__layer.addFeature(feature)
        self.__layer.updateExtents()
        self.__onConfirmClose()

    def __updateList(self):
        """
        To update the snapping options of the layer
        """
        noUse, enabled, snappingType, unitType, tolerance, avoidIntersection = \
            QgsProject.instance().snapSettingsForLayer(self.__layer.id())
        self.__layerConfig = QgsSnappingUtils.LayerConfig(
            self.__layer, QgsPointLocator.Vertex, tolerance, unitType)
        if not enabled or tolerance == 0:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Error"),
                QCoreApplication.translate(
                    "VDLTools", "This layer has no snapping options"),
                level=QgsMessageBar.CRITICAL)

    def canvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """
        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            f = Finder.findClosestFeatureAt(event.mapPoint(),
                                            self.__layerConfig, self)
            if f is not None and self.__lastFeatureId != f.id():
                self.__lastFeatureId = f.id()
                self.__layer.setSelectedFeatures([f.id()])
            if f is None:
                self.__layer.removeSelection()
                self.__lastFeatureId = None
        elif self.__findVertex:
            self.__rubberBand.reset()
            closest = self.__selectedFeature.geometry().closestVertex(
                event.mapPoint())
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setIcon(4)
            self.__rubberBand.setIconSize(20)
            self.__rubberBand.setToGeometry(
                QgsGeometry().fromPoint(closest[0]), None)
        elif self.__onMove:
            if self.__rubberBand:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(event.mapPoint())
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(event.mapPoint())
            else:
                self.__pointPreview(event.mapPoint())
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setWidth(2)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(20)
            if self.__rubberSnap:
                self.__rubberSnap.reset()
            else:
                self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point)
            self.__rubberSnap.setColor(color)
            self.__rubberSnap.setWidth(2)
            self.__rubberSnap.setIconSize(20)
            match = Finder.snap(event.mapPoint(), self.__canvas, True)
            if match.hasVertex():
                if match.layer():
                    self.__rubberSnap.setIcon(4)
                    self.__rubberSnap.setToGeometry(
                        QgsGeometry().fromPoint(match.point()), None)
                else:
                    self.__rubberSnap.setIcon(1)
                    self.__rubberSnap.setToGeometry(
                        QgsGeometry().fromPoint(match.point()), None)
            if match.hasEdge():
                self.__rubberSnap.setIcon(3)
                self.__rubberSnap.setToGeometry(
                    QgsGeometry().fromPoint(match.point()), None)

            # if self.__counter > 2:
            #     if self.__rubberSnap:
            #         self.__rubberSnap.reset()
            #     else:
            #         self.__rubberSnap = QgsRubberBand(self.__canvas, QGis.Point)
            #     self.__rubberSnap.setColor(color)
            #     self.__rubberSnap.setWidth(2)
            #     self.__rubberSnap.setIconSize(20)
            #     snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList)
            #     if snappedIntersection is None:
            #         snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList)
            #         if snappedPoint is not None:
            #             self.__rubberSnap.setIcon(4)
            #             self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedPoint), None)
            #     else:
            #         self.__rubberSnap.setIcon(1)
            #         self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(snappedIntersection), None)
            #     self.__counter = 0
            # else:
            #     self.__counter += 1

    def canvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            found_features = self.__layer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) < 1:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "One feature at a time"),
                        level=QgsMessageBar.INFO)
                    return
                self.__selectedFeature = found_features[0]
                if self.__layer.geometryType() != QGis.Point:
                    self.__findVertex = 1
                    self.__rubberBand = QgsRubberBand(self.__canvas,
                                                      QGis.Point)
                else:
                    self.__onMove = 1
                    # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface)
        elif self.__findVertex:
            self.__findVertex = 0
            closest = self.__selectedFeature.geometry().closestVertex(
                event.mapPoint())
            self.__selectedVertex = closest[1]
            self.__onMove = 1
            # self.__snapperList, self.__layerList = Finder.updateSnapperList(self.__iface)
        elif self.__onMove:
            self.__onMove = 0
            mapPoint = event.mapPoint()
            match = Finder.snap(event.mapPoint(), self.__canvas, True)
            if match.hasVertex() or match.hasEdge():
                mapPoint = match.point()
            # snappedIntersection = Finder.snapToIntersection(event.mapPoint(), self, self.__layerList)
            # if snappedIntersection is None:
            #     snappedPoint = Finder.snapToLayers(event.mapPoint(), self.__snapperList)
            #     if snappedPoint is not None:
            #         mapPoint = snappedPoint
            # else:
            #     mapPoint = snappedIntersection
            self.__isEditing = 1
            if self.__rubberBand:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(mapPoint)
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(mapPoint)
            else:
                self.__pointPreview(mapPoint)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setWidth(2)
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(20)
            self.__confDlg = MoveConfirmDialog()
            self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove)
            self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy)
            self.__confDlg.cancelButton().clicked.connect(
                self.__onConfirmClose)
            self.__confDlg.show()
Example #21
0
class DistanceMapTool(QgsMapTool):
    def __init__(self, iface):
        self.iface = iface
        self.lineLayer = MemoryLayers(iface).lineLayer()
        self.settings = MySettings()
        QgsMapTool.__init__(self, iface.mapCanvas())

    def activate(self):
        QgsMapTool.activate(self)
        self.rubber = QgsRubberBand(self.canvas(), QGis.Point)
        self.rubber.setColor(self.settings.value("rubberColor"))
        self.rubber.setIcon(self.settings.value("rubberIcon"))
        self.rubber.setIconSize(self.settings.value("rubberSize"))
        self.messageWidget = self.iface.messageBar().createMessage(
            "Intersect It", "Not snapped.")
        self.messageWidgetExist = True
        self.messageWidget.destroyed.connect(self.messageWidgetRemoved)

    def deactivate(self):
        self.iface.messageBar().popWidget(self.messageWidget)
        self.rubber.reset()
        QgsMapTool.deactivate(self)

    def messageWidgetRemoved(self):
        self.messageWidgetExist = False

    def displaySnapInfo(self, match=None):
        if not self.messageWidgetExist:
            return
        if match is None:
            message = "No snap"
        else:
            message = 'Snapped to: <b>{}</b>'.format(match.layer())
        self.messageWidget.setText(message)

    def canvasMoveEvent(self, mouseEvent):
        match = self.snap_to_vertex(mouseEvent.pos())
        self.rubber.reset(QGis.Point)
        if match.type(
        ) == QgsPointLocator.Vertex and match.layer() != self.lineLayer:
            self.rubber.addPoint(match.point())
        self.displaySnapInfo(match)

    def canvasPressEvent(self, mouseEvent):
        if mouseEvent.button() != Qt.LeftButton:
            return
        match = self.snap_to_vertex(mouseEvent.pos())
        if match.type() != QgsPointLocator.Vertex and match.layer(
        ) != self.lineLayer:
            point = self.toMapCoordinates(mouseEvent.pos())
        else:
            point = match.point()
        self.rubber.addPoint(point)
        distance = Distance(self.iface, point, 1)
        dlg = DistanceDialog(distance, self.canvas())
        if dlg.exec_():
            distance.save()
        self.rubber.reset()

    def snap_to_vertex(self, pos):
        """ Temporarily override snapping config and snap to vertices and edges
         of any editable vector layer, to allow selection of node for editing
         (if snapped to edge, it would offer creation of a new vertex there).
        """
        map_point = self.toMapCoordinates(pos)
        tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings())
        snap_type = QgsPointLocator.Type(QgsPointLocator.Vertex)

        snap_layers = []
        for layer in self.canvas().layers():
            if not isinstance(layer, QgsVectorLayer):
                continue
            snap_layers.append(
                QgsSnappingUtils.LayerConfig(layer, snap_type, tol,
                                             QgsTolerance.ProjectUnits))

        snap_util = self.canvas().snappingUtils()
        old_layers = snap_util.layers()
        old_mode = snap_util.snapToMapMode()
        old_inter = snap_util.snapOnIntersections()
        snap_util.setLayers(snap_layers)
        snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)
        snap_util.setSnapOnIntersections(True)
        m = snap_util.snapToMap(map_point)
        snap_util.setLayers(old_layers)
        snap_util.setSnapToMapMode(old_mode)
        snap_util.setSnapOnIntersections(old_inter)
        return m
Example #22
0
class MoveTool(QgsMapToolAdvancedDigitizing):
    """
    Map tool class to move or copy an object
    """

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapToolAdvancedDigitizing.__init__(self,  iface.mapCanvas(), iface.cadDockWidget())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/move_icon.png'
        self.text = QCoreApplication.translate("VDLTools", "Move/Copy a feature")
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__findVertex = False
        self.__onMove = False
        self.__layer = None
        self.__confDlg = None
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__rubberBand = None
        self.__rubberSnap = None
        self.__newFeature = None
        self.__selectedVertex = None

    def activate(self):
        """
        When the action is selected
        """
        QgsMapToolAdvancedDigitizing.activate(self)
        if self.__layer.geometryType() == QGis.Point:
            self.setMode(self.CaptureLine)
        else:
            self.setMode(self.CaptureNone)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__cancel()
        QgsMapToolAdvancedDigitizing.deactivate(self)

    def toolName(self):
        """
        To get the tool name
        :return: tool name
        """
        return QCoreApplication.translate("VDLTools", "Move/Copy")

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def __cancel(self):
        """
        To cancel used variables
        """
        if self.__rubberBand is not None:
            self.canvas().scene().removeItem(self.__rubberBand)
            self.__rubberBand.reset()
            self.__rubberBand = None
        if self.__rubberSnap is not None:
            self.canvas().scene().removeItem(self.__rubberSnap)
            self.__rubberSnap.reset()
            self.__rubberSnap = None
        self.__isEditing = False
        self.__findVertex = False
        self.__onMove = False
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__confDlg = None
        self.__newFeature = None
        self.__selectedVertex = None
        self.__layer.removeSelection()
        if self.__layer.geometryType() == QGis.Point:
            self.setMode(self.CaptureLine)
        else:
            self.setMode(self.CaptureNone)

    def __removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
            else:
                Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type() == QgsMapLayer.VectorLayer:
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
                else:
                    Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = layer

            if self.__layer.geometryType() == QGis.Point:
                self.setMode(self.CaptureLine)
            else:
                self.setMode(self.CaptureNone)

            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.canvas().mapTool() == self:
                    self.__iface.actionPan().trigger()
            return
        self.action().setEnabled(False)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()
        self.__removeLayer()

    def __pointPreview(self, point):
        """
        To create a point geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(), self.__iface)
        self.__newFeature = QgsPointV2(point.x(), point.y())
        self.__newFeature.addZValue(point_v2.z())
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point)
        self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()), None)

    def __linePreview(self, point):
        """
        To create a line geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface)
        vertex = QgsPointV2()
        line_v2.pointAt(self.__selectedVertex, vertex)
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line)
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        if isinstance(curved, (list, tuple)):
            self.__newFeature = QgsCompoundCurveV2()
            for pos in range(line_v2.nCurves()):
                curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos), dx, dy)
                self.__newFeature.addCurve(curve_v2)
                if pos == 0:
                    self.__rubberBand.setToGeometry(QgsGeometry(curve_v2.curveToLine()), None)
                else:
                    self.__rubberBand.addGeometry(QgsGeometry(curve_v2.curveToLine()), None)
        else:
            self.__newFeature = self.__newCurve(curved, line_v2, dx, dy)
            self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.curveToLine()), None)

    @staticmethod
    def __newCurve(curved, line_v2, dx, dy):
        """
        To create a new moved line
        :param curved: if the line is curved
        :param line_v2: the original line
        :param dx: x translation
        :param dy: y translation
        :return: the new line
        """
        if curved:
            newCurve = QgsCircularStringV2()
        else:
            newCurve = QgsLineStringV2()
        points = []
        for pos in range(line_v2.numPoints()):
            x = line_v2.pointN(pos).x() - dx
            y = line_v2.pointN(pos).y() - dy
            pt = QgsPointV2(x, y)
            pt.addZValue(line_v2.pointN(pos).z())
            points.append(pt)
        newCurve.setPoints(points)
        return newCurve

    def __polygonPreview(self, point):
        """
        To create a polygon geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        polygon_v2, curved = GeometryV2.asPolygonV2(self.__selectedFeature.geometry(), self.__iface)
        vertex = polygon_v2.vertexAt(GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex))
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        self.__newFeature = QgsCurvePolygonV2()
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line)
        line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy)
        self.__newFeature.setExteriorRing(line_v2)
        self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()), None)
        for num in range(polygon_v2.numInteriorRings()):
            line_v2 = self.__newCurve(curved[num+1], polygon_v2.interiorRing(num), dx, dy)
            self.__newFeature.addInteriorRing(line_v2)
            self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()), None)

    def __onConfirmCancel(self):
        """
        When the Cancel button in Move Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __onConfirmMove(self):
        """
        When the Move button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0)
        self.__layer.changeGeometry(self.__selectedFeature.id(), geometry)
        self.__confDlg.accept()
        self.__cancel()

    def __onConfirmCopy(self):
        """
        When the Copy button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Geos geometry problem"), level=QgsMessageBar.CRITICAL, duration=0)
        feature = QgsFeature(self.__layer.pendingFields())
        feature.setGeometry(geometry)
        primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn()
        for field in self.__selectedFeature.fields():
            if field.name() != primaryKey:
                feature.setAttribute(field.name(), self.__selectedFeature.attribute(field.name()))
        if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \
                QgsEditFormConfig.SuppressOn:
            self.__iface.openFeatureForm(self.__layer, feature)
        else:
            self.__layer.addFeature(feature)
        self.__confDlg.accept()
        self.__cancel()

    def keyReleaseEvent(self, event):
        """
        When keyboard is pressed
        :param event: keyboard event
        """
        if event.key() == Qt.Key_Escape:
            self.__cancel()

    def cadCanvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """

        if type(event) == QMoveEvent:
            map_point = self.toMapCoordinates(event.pos())
        else:
            map_point = event.mapPoint()

        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10,
                                                       QgsTolerance.Pixels)
            f_l = Finder.findClosestFeatureAt(map_point, self.canvas(), [laySettings])
            if f_l is not None and self.__lastFeatureId != f_l[0].id():
                self.__lastFeatureId = f_l[0].id()
                self.__layer.setSelectedFeatures([f_l[0].id()])
            if f_l is None:
                self.__layer.removeSelection()
                self.__lastFeatureId = None
        elif self.__findVertex:
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            closest = self.__selectedFeature.geometry().closestVertex(map_point)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setIcon(4)
            self.__rubberBand.setIconSize(20)
            self.__rubberBand.setToGeometry(QgsGeometry().fromPoint(closest[0]), None)
        elif self.__onMove:
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(map_point)
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(map_point)
            else:
                self.__pointPreview(map_point)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setWidth(2)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(8)
            if self.__rubberSnap is not None:
                self.__rubberSnap.reset()
            else:
                self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point)
            self.__rubberSnap.setColor(color)
            self.__rubberSnap.setWidth(2)
            self.__rubberSnap.setIconSize(20)
            match = Finder.snap(map_point, self.canvas())
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer():
                        self.__rubberSnap.setIcon(4)
                    else:
                        self.__rubberSnap.setIcon(1)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(point, self.canvas(), self)
                    if intersection is not None:
                        self.__rubberSnap.setIcon(1)
                        point = intersection
                    else:
                        self.__rubberSnap.setIcon(3)
                self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point), None)

    def cadCanvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            found_features = self.__layer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO)
                    return
                self.__selectedFeature = found_features[0]
                if self.__layer.geometryType() != QGis.Point:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "Select vertex for moving (ESC to undo)"),
                        level=QgsMessageBar.INFO, duration=3)
                    self.__findVertex = True
                    self.setMode(self.CaptureLine)
                    self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point)
                else:
                    self.setMode(self.CaptureNone)
                    self.__onMove = True
        elif self.__findVertex:
            self.__findVertex = False
            self.setMode(self.CaptureNone)
            closest = self.__selectedFeature.geometry().closestVertex(event.mapPoint())
            self.__selectedVertex = closest[1]
            self.__onMove = True
        elif self.__onMove:
            self.__onMove = False
            mapPoint = event.mapPoint()
            match = Finder.snap(event.mapPoint(), self.canvas())
            if match.hasVertex() or match.hasEdge():
                mapPoint = match.point()
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(mapPoint, self.canvas(), self)
                    if intersection is not None:
                        mapPoint = intersection
            self.__isEditing = True
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(mapPoint)
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(mapPoint)
            else:
                self.__pointPreview(mapPoint)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setWidth(2)
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(20)
            self.__confDlg = MoveConfirmDialog()
            self.__confDlg.rejected.connect(self.__cancel)
            self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove)
            self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy)
            self.__confDlg.cancelButton().clicked.connect(self.__onConfirmCancel)
            self.__confDlg.show()
class nominatim_dlg(QDockWidget, Ui_search):

    def getHttp(self, uri, params):
        QgsApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            rq = QUrl(uri)
            q = QUrlQuery()
            for (k, v) in params.items():
                q.addQueryItem(k, v)

            rq.setQuery(q)
            req = QNetworkRequest(rq)

            try:
                reply = self.nominatim_networkAccessManager.blockingGet(req)
                resource = reply.content().data().decode('utf8')
                r = json.loads(resource)

                if (isinstance(r, list)):
                    self.populateTable(r)
                else:
                    self.populateTable([r])
            except:
                self.tableResult.clearContents()

        finally:
            QgsApplication.restoreOverrideCursor()

    def searchJson(self, params, user, options, options2):
        contents = str(options).strip()
        items = contents.split(' ')

        for (k, v) in options2.items():
            if k in ['viewbox']:
                params["bounded"] = "1"
            params[k] = v

        pairs = []
        for item in items:
            pair = item.split('=', 1)
            if (pair != [''] and pair != [] and len(pair) > 1):
                pairs.append(pair)

        for (k, v) in pairs:
            if k in ['viewbox', 'countrycodes', 'limit', 'exclude_place_ids', 'addressdetails',
                     'exclude_place_ids', 'bounded', 'routewidth',
                     'osm_type', 'osm_id'] and not(k in options2.keys()):
                params[k] = v

            if k in ['viewbox']:
                params["bounded"] = "1"

        params["polygon_text"] = "1"
        params["format"] = "json"

        uri = 'https://nominatim.openstreetmap.org/search'

        self.getHttp(uri, params)

    def findNearbyJSON(self, params, user, options):
        uri = "https://nominatim.openstreetmap.org/reverse"
        params["format"] = "json"
        self.getHttp(uri, params)

    """
     Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock
    """
    def eventFilter(self, obj, event):
        typ = event.type()
        if typ == event.Leave:
            try:
                self.plugin.canvas.scene().removeItem(self.rubber)
            except:
                pass

        return False

    def __init__(self, parent, plugin):
        self.plugin = plugin
        QDockWidget.__init__(self, parent)
        self.setupUi(self)

        self.btnApply.setIcon(QIcon(":plugins/nominatim/arrow_green.png"))
        self.btnMask.setIcon(QIcon(":plugins/nominatim/add_mask.png"))
        self.btnLayer.setIcon(QIcon(":plugins/nominatim/add_layer.png"))

        self.tableResult.installEventFilter(self)  # cf. eventFilter method
        self.tableResult.cellDoubleClicked.connect(self.onChoose)
        self.tableResult.cellEntered.connect(self.cellEntered)

        self.editSearch.returnPressed.connect(self.onReturnPressed)
        self.btnSearch.clicked.connect(self.onReturnPressed)
        self.btnApply.clicked.connect(self.onApply)
        self.btnHelp.clicked.connect(self.plugin.do_help)
        self.btnLocalize.clicked.connect(self.doLocalize)
        self.btnMask.clicked.connect(self.onMask)
        self.btnLayer.clicked.connect(self.onLayer)

        self.MultiPolygonLayerId = None
        self.LineLayerId = None
        self.PointLayerId = None

        try:
            self.cbExtent.setChecked(self.plugin.limitSearchToExtent)
        except:
            self.cbExtent.setChecked(self.plugin.limitSearchToExtent)

        self.currentExtent = self.plugin.canvas.extent()

        self.tableResult.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

        try:
            self.editSearch.setText(self.plugin.lastSearch)
        except:
            pass

        try:
            if self.plugin.localiseOnStartup:
                self.doLocalize()
        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

        self.nominatim_networkAccessManager = QgsNetworkAccessManager.instance()

    def cellEntered(self, row, col):
        item = self.tableResult.item(row, 0)

        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
            self.showItem(item)
        except:
            pass

    def onLayer(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doLayer(item)

    def onMask(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doMask(item)

    def populateRow(self, item, idx):
        id = item['place_id']
        name = item['display_name']

        try:
            className = QApplication.translate("nominatim", item['class'], None)
        except:
            className = ""

        try:
            typeName = QApplication.translate("nominatim", item['type'], None)
        except:
            typeName = ""

        try:
            wkt = item['geotext']
        except:
            wkt = None

        try:
            osm_type = item['osm_type']
        except:
            osm_type = None

        bbox = {}
        if osm_type == "node":
            lat = item['lat']
            lng = item['lon']

            poFD = ogr.FeatureDefn("Point")
            poFD.SetGeomType(ogr.wkbPoint)

            oFLD = ogr.FieldDefn('id', ogr.OFTString)
            poFD.AddFieldDefn(oFLD)
            oFLD = ogr.FieldDefn('name', ogr.OFTString)
            poFD.AddFieldDefn(oFLD)

            ogrFeature = ogr.Feature(poFD)
            wkt = "POINT({} {})".format(lng, lat)
            ogrGeom = ogr.CreateGeometryFromWkt(wkt)
        else:
            try:
                bbox = item['boundingbox']

                poFD = ogr.FeatureDefn("Rectangle")
                poFD.SetGeomType(ogr.wkbPolygon)

                oFLD = ogr.FieldDefn('id', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn('name', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                if wkt is None:
                    wkt = "POLYGON(({b[2]} {b[0]}, {b[2]} {b[1]}, {b[3]} {b[1]}, {b[3]} {b[0]}, {b[2]} {b[0]}))".format(b=bbox)

                ogrGeom = ogr.CreateGeometryFromWkt(wkt)
            except:
                lat = item['lat']
                lng = item['lon']

                poFD = ogr.FeatureDefn("Point")
                poFD.SetGeomType(ogr.wkbPoint)

                oFLD = ogr.FieldDefn('id', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn('name', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                wkt = "POINT({} {})".format(lng, lat)
                ogrGeom = ogr.CreateGeometryFromWkt(wkt)

        ogrFeature.SetGeometry(ogrGeom)
        ogrFeature.SetFID(int(idx + 1))
        ogrFeature.SetField(str('id'), str(id))
        ogrFeature.SetField(str('name'), name)

        item = QTableWidgetItem(name)
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setData(Qt.UserRole, ogrFeature)
        self.tableResult.setItem(idx, 0, item)

        itemLibelle = QTableWidgetItem(className)
        itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 1, itemLibelle)

        itemType = QTableWidgetItem(typeName)
        itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 2, itemType)

    def populateTable(self, r):
        idx = 0
        self.tableResult.clearContents()
        self.tableResult.setRowCount(len(r))
        for item in r:
            self.populateRow(item, idx)
            idx = idx + 1

    def doLocalize(self):
        try:
            # center
            bbox = self.plugin.canvas.extent()
            sourceCrs = self.plugin.canvas.mapSettings().destinationCrs()
            targetCrs = QgsCoordinateReferenceSystem(4326)
            xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance())
            bbox = xform.transform(bbox)

            params = {"lon": str(bbox.center().x()), "lat": str(bbox.center().y()), "zoom": "10"}
            self.findNearbyJSON(params, self.plugin.gnUsername, self.plugin.gnOptions)

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

    def onReturnPressed(self):
        try:
            txt = self.editSearch.text().strip()
            self.plugin.lastSearch = self.editSearch.text()
            self.plugin.limitSearchToExtent = (self.cbExtent.isChecked())
            options = self.plugin.gnOptions

            options2 = {}
            if self.plugin.limitSearchToExtent:
                sourceCrs = self.plugin.canvas.mapSettings().destinationCrs()
                targetCrs = QgsCoordinateReferenceSystem()
                targetCrs.createFromSrid(4326)
                xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance())
                geom = xform.transform(self.plugin.canvas.extent())
                vb = "{},{},{},{}".format(geom.xMinimum(), geom.yMaximum(), geom.xMaximum(), geom.yMinimum())
                options2 = {'viewbox': vb}

            params = {'q': txt, 'addressdetails': '0'}
            self.searchJson(params, self.plugin.gnUsername, options, options2)

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

    def onChoose(self, row, col):
        item = self.tableResult.item(row, 0)
        self.go(item)

    def onApply(self):
        for item in self.tableResult.selectedItems():
            self.go(item)
            break

    def transform(self, geom):
        sourceSRS = QgsCoordinateReferenceSystem(4326)
        mapCrs = self.plugin.canvas.mapSettings().destinationCrs()
        trsf = QgsCoordinateTransform(sourceSRS, mapCrs, QgsProject.instance())
        try:
            geom.transform(trsf)
        except TypeError:
            QgsMessageLog.logMessage("Nominatim - transformation error. Check map projection.",
                                     "Extensions")

    def getBBox(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint):
            mapextent = self.plugin.canvas.extent()
            ww = mapextent.width() / 100
            mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

            x = geom.boundingBox().center().x()
            y = geom.boundingBox().center().y()

            ww = 50.0
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet:
                ww = 150
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees:
                ww = 0.0005

            bbox = QgsRectangle(x - 10 * ww, y - 10 * ww, x + 10 * ww, y + 10 * ww)
            return bbox
        else:
            bbox = geom.boundingBox()
            rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(),
                                      bbox.xMaximum(), bbox.yMaximum())
            return rubberRect

    def showItem(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint):
            self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setIcon(self.rubber.ICON_CIRCLE)
            self.rubber.setIconSize(15)
            self.rubber.setWidth(2)
            self.rubber.setToGeometry(geom, None)
        else:
            # dont show if it is larger than the canvas
            if self.plugin.canvas.extent().contains(geom.boundingBox()):
                pass
            else:
                geom = geom.intersection(QgsGeometry.fromRect(self.plugin.canvas.extent()))

            self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setWidth(4)
            self.rubber.setToGeometry(geom, None)

    def go(self, item, zoom=True):
        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
        except:
            pass

        if zoom:
            bbox = self.getBBox(item)
            self.plugin.canvas.setExtent(bbox)

        self.plugin.canvas.refresh()
        self.showItem(item)

    def doMask(self, item):
        mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

        ogrFeature = item.data(Qt.UserRole)
        layerName = "OSM "+ogrFeature.GetFieldAsString('id')
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        if (geom.type() == QgsWkbTypes.PolygonGeometry):
            try:
                try:
                    from mask import aeag_mask
                except:
                    from mask_plugin import aeag_mask

                aeag_mask.do(mapcrs, {geom}, "Mask "+layerName)

            except:
                geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
                self.transform(geom)
                toCrs = self.plugin.canvas.mapSettings().destinationCrs()

                larg = max(geom.boundingBox().width(), geom.boundingBox().height())
                x = geom.boundingBox().center().x()
                y = geom.boundingBox().center().y()
                rect = QgsRectangle(x-larg, y-larg, x+larg, y+larg)  # geom.boundingBox()
                rect.scale(4)
                mask = QgsGeometry.fromRect(rect)

                mask = mask.difference(geom)

                maskLayer = QgsVectorLayer("MultiPolygon", "Mask "+layerName, "memory")
                maskLayer.setCrs(toCrs)
                QgsProject.instance().addMapLayer(maskLayer)
                pr = maskLayer.dataProvider()

                fields = QgsFields()
                fields.append(QgsField("id", QVariant.String))
                fields.append(QgsField("name", QVariant.String))
                fet = QgsFeature()
                fet.initAttributes(2)
                fet.setGeometry(mask)
                fet.setFields(fields)
                fet.setAttribute("id", (ogrFeature.GetFieldAsString('id')))
                fet.setAttribute("name", (ogrFeature.GetFieldAsString('name')))

                pr.addAttributes(fields.toList())

                maskLayer.startEditing()
                pr.addFeatures([fet])
                maskLayer.commitChanges()
                maskLayer.updateExtents()

                # transparence, epaisseur
                renderer = maskLayer.renderer()
                s = renderer.symbol()
                s.setOpacity(0.90)
                s.setColor(QColor(255, 255, 255))
                if isinstance(s, QgsLineSymbol):
                    s.setWidth(0)

                layerTree = QgsProject.instance().layerTreeRoot().findLayer(maskLayer)
                if layerTree:
                    self.plugin.iface.layerTreeView().layerTreeModel()\
                        .refreshLayerLegend(layerTree)  # Refresh legend

            self.go(item)

    def doLayer(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        self.transform(geom)

        fields = QgsFields()
        fields.append(QgsField("id", QVariant.String))
        fields.append(QgsField("name", QVariant.String))
        fet = QgsFeature()
        fet.initAttributes(2)
        fet.setFields(fields)
        fet.setGeometry(geom)
        fet.setAttribute("id", (ogrFeature.GetFieldAsString('id')))
        fet.setAttribute("name", (ogrFeature.GetFieldAsString('name')))

        vl = None
        if not self.plugin.singleLayer:
            if geom.type() == QgsWkbTypes.PolygonGeometry:
                layerName = "OSMPlaceSearch Polygon"
                layerId = self.MultiPolygonLayerId
            if geom.type() == QgsWkbTypes.LineGeometry:
                layerName = "OSMPlaceSearch Line"
                layerId = self.LineLayerId
            if geom.type() == QgsWkbTypes.PointGeometry:
                layerName = "OSMPlaceSearch Point"
                layerId = self.PointLayerId

            vl = QgsProject.instance().mapLayer(layerId)
            if vl is not None:
                pr = vl.dataProvider()
            else:
                if geom.type() == QgsWkbTypes.PolygonGeometry:
                    vl = QgsVectorLayer("MultiPolygon", layerName, "memory")
                    self.MultiPolygonLayerId = vl.id()
                if geom.type() == QgsWkbTypes.LineGeometry:
                    vl = QgsVectorLayer("MultiLineString", layerName, "memory")
                    self.LineLayerId = vl.id()
                if geom.type() == QgsWkbTypes.PointGeometry:
                    vl = QgsVectorLayer("Point", layerName, "memory")
                    self.PointLayerId = vl.id()

                if vl is not None:
                    pr = vl.dataProvider()
                    # ajout de champs
                    pr.addAttributes(fields.toList())

                QgsProject.instance().addMapLayer(vl)
        else:
            layerName = "OSM "+ogrFeature.GetFieldAsString('id')

            # creer une nouvelle couche si n'existe pas encore
            if geom.type() == QgsWkbTypes.PolygonGeometry:
                vl = QgsVectorLayer("MultiPolygon", layerName, "memory")
            if geom.type() == QgsWkbTypes.LineGeometry:
                vl = QgsVectorLayer("MultiLineString", layerName, "memory")
            if geom.type() == QgsWkbTypes.PointGeometry:
                vl = QgsVectorLayer("Point", layerName, "memory")

            if vl is not None:
                pr = vl.dataProvider()
                # ajout de champs
                pr.addAttributes(fields.toList())
                vl.setCrs(self.plugin.canvas.mapSettings().destinationCrs())

            QgsProject.instance().addMapLayer(vl)

        if vl is not None:
            vl.setProviderEncoding('UTF-8')
            vl.startEditing()
            pr.addFeatures([fet])
            vl.commitChanges()

            # mise a jour etendue de la couche
            vl.updateExtents()

            layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl)
            if layerTree:
                self.plugin.iface.layerTreeView()\
                    .layerTreeModel().refreshLayerLegend(layerTree)  # Refresh legend

            self.go(item, False)
Example #24
0
class InterpolateTool(QgsMapToolAdvancedDigitizing):
    """
    Map tool class to interpolate an elevation in the middle of a segment
    """
    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(),
                                              iface.cadDockWidget())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/interpolate_icon.png'
        self.text = QCoreApplication.translate(
            "VDLTools",
            "Interpolate the elevation of a vertex and a point in the middle of a line"
        )
        self.__layer = None
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__lastFeatureId = None
        self.__layerList = None
        self.__lastLayer = None
        self.__confDlg = None
        self.__mapPoint = None
        self.__rubber = None
        self.__selectedFeature = None
        self.__findVertex = False

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapToolAdvancedDigitizing.activate(self)
        self.__updateList()
        self.__rubber = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setWidth(2)
        self.__rubber.setIconSize(20)
        self.canvas().layersChanged.connect(self.__updateList)
        self.canvas().scaleChanged.connect(self.__updateList)
        self.setMode(self.CaptureLine)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__done()
        self.__cancel()
        self.__rubber = None
        Signal.safelyDisconnect(self.canvas().layersChanged, self.__updateList)
        Signal.safelyDisconnect(self.canvas().scaleChanged, self.__updateList)
        QgsMapToolAdvancedDigitizing.deactivate(self)

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()

    def __done(self):
        """
        When the edition is finished
        """
        self.__isEditing = False
        self.__confDlg = None
        self.__mapPoint = None

    def __cancel(self):
        """
        To cancel used variables
        """
        self.__findVertex = False
        if self.__lastLayer is not None:
            self.__lastLayer.removeSelection()
            self.__lastLayer = None
        if self.__rubber is not None:
            self.__rubber.reset()
        self.__lastFeatureId = None
        self.__selectedFeature = None

    def __removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                Signal.safelyDisconnect(self.__layer.editingStopped,
                                        self.stopEditing)
            else:
                Signal.safelyDisconnect(self.__layer.editingStarted,
                                        self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type(
        ) == QgsMapLayer.VectorLayer and layer.geometryType() == QGis.Point:
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    Signal.safelyDisconnect(self.__layer.editingStopped,
                                            self.stopEditing)
                else:
                    Signal.safelyDisconnect(self.__layer.editingStarted,
                                            self.startEditing)
            self.__layer = layer
            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.canvas().mapTool() == self:
                    self.__iface.actionPan().trigger()
            return
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()
        self.action().setEnabled(False)
        self.__removeLayer()

    def __updateList(self):
        """
        To update the line layers list that we can use for interpolation
        """
        self.__layerList = []
        for layer in self.canvas().layers():
            if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType() \
                    and layer.geometryType() == QGis.Line:
                self.__layerList.append(
                    QgsSnappingUtils.LayerConfig(layer, QgsPointLocator.All,
                                                 10, QgsTolerance.Pixels))

    def keyReleaseEvent(self, event):
        """
        When keyboard is pressed
        :param event: keyboard event
        """
        if event.key() == Qt.Key_Escape:
            self.__done()
            self.__cancel()

    def cadCanvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """

        if type(event) == QMoveEvent:
            map_point = self.toMapCoordinates(event.pos())
        else:
            map_point = event.mapPoint()

        if not self.__isEditing and not self.__findVertex and self.__layerList is not None:
            f_l = Finder.findClosestFeatureAt(map_point, self.canvas(),
                                              self.__layerList)

            if f_l is not None and self.__lastFeatureId != f_l[0].id():
                f = f_l[0]
                self.__lastFeatureId = f.id()
                if self.__lastLayer is not None:
                    self.__lastLayer.removeSelection()
                self.__lastLayer = f_l[1]
                self.__lastLayer.setSelectedFeatures([f.id()])
            if f_l is None and self.__lastLayer is not None:
                self.__lastLayer.removeSelection()
                self.__lastFeatureId = None
        elif self.__findVertex:
            self.__rubber.reset()
            snap_layers = Finder.getLayersSettings(self.canvas(),
                                                   [QGis.Line, QGis.Polygon],
                                                   QgsPointLocator.All)
            match = Finder.snap(map_point, self.canvas(), snap_layers)
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        self.__rubber.setIcon(4)
                        self.__rubber.setToGeometry(
                            QgsGeometry().fromPoint(point), None)
                    else:
                        intersection = Finder.snapCurvedIntersections(
                            point, self.canvas(), self,
                            self.__selectedFeature.id())
                        if intersection is not None:
                            self.__rubber.setIcon(1)
                            self.__rubber.setToGeometry(
                                QgsGeometry().fromPoint(intersection), None)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(
                        point, self.canvas(), self,
                        self.__selectedFeature.id())
                    if intersection is not None:
                        self.__rubber.setIcon(1)
                        self.__rubber.setToGeometry(
                            QgsGeometry().fromPoint(intersection), None)
                    elif self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        self.__rubber.setIcon(3)
                        self.__rubber.setToGeometry(
                            QgsGeometry().fromPoint(point), None)

    def cadCanvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        if self.__lastLayer is not None and not self.__findVertex:
            found_features = self.__lastLayer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "One feature at a time"),
                        level=QgsMessageBar.INFO)
                    return
                self.__selectedFeature = found_features[0]

                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate(
                        "VDLTools",
                        "Select the position for interpolation (ESC to undo)"),
                    level=QgsMessageBar.INFO,
                    duration=3)
                self.setMode(self.CaptureNone)
                self.__findVertex = True
        elif self.__findVertex:
            self.__rubber.reset()
            snap_layers = Finder.getLayersSettings(self.canvas(),
                                                   [QGis.Line, QGis.Polygon],
                                                   QgsPointLocator.All)
            match = Finder.snap(event.mapPoint(), self.canvas(), snap_layers)
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                ok = False
                noVertex = False
                if match.hasVertex():
                    if match.layer() is not None and self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():

                        ok = True
                        noVertex = True
                    else:
                        intersection = Finder.snapCurvedIntersections(
                            point, self.canvas(), self,
                            self.__selectedFeature.id())
                        if intersection is not None:
                            point = intersection
                            ok = True
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(
                        point, self.canvas(), self,
                        self.__selectedFeature.id())
                    if intersection is not None:
                        point = intersection
                        ok = True
                    elif self.__selectedFeature.id() == match.featureId() \
                            and match.layer().id() == self.__lastLayer.id():
                        ok = True
                if ok:
                    self.__isEditing = True
                    self.__findVertex = False
                    self.__mapPoint = point
                    if noVertex:
                        self.__ok(False, True)
                    else:
                        self.__confDlg = InterpolateConfirmDialog()
                        if self.__lastLayer.isEditable():
                            self.__confDlg.setMainLabel(
                                QCoreApplication.translate(
                                    "VDLTools", "What do you want to do ?"))
                            self.__confDlg.setAllLabel(
                                QCoreApplication.translate(
                                    "VDLTools", "Create point and new vertex"))
                            self.__confDlg.setVtLabel(
                                QCoreApplication.translate(
                                    "VDLTools", "Create only the vertex"))
                        self.__confDlg.rejected.connect(self.__done)
                        self.__confDlg.okButton().clicked.connect(
                            self.__onConfirmOk)
                        self.__confDlg.cancelButton().clicked.connect(
                            self.__onConfirmCancel)
                        self.__confDlg.show()
            else:
                self.__done()
                self.__cancel()

    def __onConfirmCancel(self):
        """
        When the Cancel button in Interpolate Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __onConfirmOk(self):
        """
        When the Ok button in Interpolate Confirm Dialog is pushed
        """
        checkedId = self.__confDlg.getCheckedId()
        self.__confDlg.accept()

        withVertex = True
        withPoint = True
        if checkedId == 1:
            withVertex = False
        else:
            if not self.__lastLayer.isEditable():
                self.__lastLayer.startEditing()
        if checkedId == 2:
            withPoint = False

        self.__ok(withVertex, withPoint)

    def __ok(self, withVertex, withPoint):
        """
        To realize the interpolation
        :param withVertex: if we want a new interpolated vertex
        :param withPoint: if we want a new interpolated point
        """
        line_v2, curved = GeometryV2.asLineV2(
            self.__selectedFeature.geometry(), self.__iface)
        vertex_v2 = QgsPointV2()
        vertex_id = QgsVertexId()
        line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2,
                               vertex_id, 0)

        x0 = line_v2.xAt(vertex_id.vertex - 1)
        y0 = line_v2.yAt(vertex_id.vertex - 1)
        d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y())
        x1 = line_v2.xAt(vertex_id.vertex)
        y1 = line_v2.yAt(vertex_id.vertex)
        d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y())
        z0 = line_v2.zAt(vertex_id.vertex - 1)
        z1 = line_v2.zAt(vertex_id.vertex)
        vertex_v2.addZValue(old_div((d0 * z1 + d1 * z0), (d0 + d1)))

        if withPoint:
            pt_feat = QgsFeature(self.__layer.pendingFields())
            pt_feat.setGeometry(QgsGeometry(vertex_v2))
            for i in range(len(self.__layer.pendingFields())):
                # default = self.__layer.defaultValue(i, pt_feat)
                # if default is not None:
                #     print(pt_feat.fields().at(i).name(), pt_feat.fields().at(i).defaultValueExpression(), default)
                #     print(self.__layer.defaultValueExpression(i), self.__layer.expressionField(i))

                e = QgsExpression(self.__layer.defaultValueExpression(i))
                default = e.evaluate(pt_feat)
                pt_feat.setAttribute(i, default)

            if self.__layer.editFormConfig().suppress(
            ) == QgsEditFormConfig.SuppressOn:
                self.__layer.addFeature(pt_feat)
            else:
                self.__iface.openFeatureForm(self.__layer, pt_feat)

        if withVertex:
            line_v2.insertVertex(vertex_id, vertex_v2)
            self.__lastLayer.changeGeometry(self.__selectedFeature.id(),
                                            QgsGeometry(line_v2))

            found_features = self.__lastLayer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "One feature at a time"),
                        level=QgsMessageBar.INFO)
                else:
                    self.__selectedFeature = found_features[0]
            else:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools",
                                               "No more feature selected"),
                    level=QgsMessageBar.INFO)

        self.__iface.mapCanvas().refresh()

        self.__done()
        self.__findVertex = True
class GeomapfishLocatorFilter(QgsLocatorFilter):

    USER_AGENT = b'Mozilla/5.0 QGIS GeoMapFish Locator Filter'

    changed = pyqtSignal()

    def __init__(self, service: Service, iface: QgisInterface = None):
        super().__init__()
        self.service = service.clone()
        self.rubberband = None
        self.iface = None
        self.map_canvas = None
        self.current_timer = None
        self.settings = Settings()
        self.crs = QgsCoordinateReferenceSystem(self.service.crs)

        # only get map_canvas on main thread, not when cloning
        if iface is not None:
            self.iface = iface
            self.map_canvas = iface.mapCanvas()

            self.rubberband = QgsRubberBand(self.map_canvas)
            self.reset_rubberband()

    def name(self) -> str:
        return slugify(self.displayName())

    def clone(self):
        return GeomapfishLocatorFilter(self.service)

    def displayName(self) -> str:
        return self.service.name

    def prefix(self) -> str:
        return 'gmf'

    def hasConfigWidget(self) -> bool:
        return True

    def openConfigWidget(self, parent=None):
        cfg = FilterConfigurationDialog(self.service, parent)
        if cfg.exec_():
            self.service = cfg.service.clone()
            self.crs = QgsCoordinateReferenceSystem(self.service.crs)
            self.changed.emit()

    def reset_rubberband(self):
        # this should happen on main thread only!
        self.rubberband.setColor(self.settings.value('point_color'))
        self.rubberband.setIcon(self.rubberband.ICON_CIRCLE)
        self.rubberband.setIconSize(self.settings.value('point_size'))
        self.rubberband.setWidth(self.settings.value('line_width'))
        self.rubberband.setBrushStyle(Qt.NoBrush)

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url

    def emit_bad_configuration(self, err=None):
        result = QgsLocatorResult()
        result.filter = self
        result.displayString = self.tr('Locator filter is not configured.')
        result.description = err if err else self.tr(
            'Double-click to configure it.')
        result.userData = FilterNotConfigured
        result.icon = QgsApplication.getThemeIcon('mIconWarning.svg')
        self.resultFetched.emit(result)
        return

    @pyqtSlot()
    def clear_results(self):
        if self.rubberband:
            self.rubberband.reset(QgsWkbTypes.PointGeometry)
        if self.current_timer is not None:
            self.current_timer.timeout.disconnect(self.clear_results)
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def fetchResults(self, search, context, feedback):
        self.dbg_info("start GMF locator search...")
        url = self.service.url
        if not url:
            self.emit_bad_configuration()
            return
        if len(search) < 2:
            return

        params = {
            'query': search,
            'limit': str(self.service.total_limit),
            'partitionlimit': str(self.service.category_limit)
        }
        url = self.url_with_param(url, params)
        self.dbg_info(url.url())

        _request = QNetworkRequest(url)
        _request.setRawHeader(b'User-Agent', self.USER_AGENT)
        request = QgsBlockingNetworkRequest()
        if self.service.authid:
            request.setAuthCfg(self.service.authid)

        response = request.get(_request, False, feedback)
        if response == QgsBlockingNetworkRequest.NoError:
            self.handle_response(request.reply().content())
        else:
            error_msg = request.reply().errorString()
            self.emit_bad_configuration(error_msg)
            self.info(error_msg, Qgis.Critical)
        self.finished.emit()

    def handle_response(self, content: QByteArray):
        try:
            data = json.loads(str(content.data(), encoding='utf-8'))
            # self.dbg_info(data)

            features = data['features']
            for f in features:
                json_geom = json.dumps(f['geometry'])
                ogr_geom = ogr.CreateGeometryFromJson(json_geom)
                wkt = ogr_geom.ExportToWkt()
                geometry = QgsGeometry.fromWkt(wkt)
                self.dbg_info('---------')
                self.dbg_info(
                    QgsWkbTypes.geometryDisplayString(geometry.type()))
                self.dbg_info(f.keys())
                self.dbg_info('{} {}'.format(f['properties']['layer_name'],
                                             f['properties']['label']))
                self.dbg_info(f['bbox'])
                self.dbg_info(f['geometry'])
                if geometry is None:
                    continue
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = f['properties']['label']
                result.group = self.beautify_group(
                    f['properties']['layer_name'])
                result.userData = geometry
                self.resultFetched.emit(result)

        except Exception as e:
            self.info(str(e), Qgis.Critical)
            # exc_type, exc_obj, exc_traceback = sys.exc_info()
            # #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1]
            # #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical)
            # self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical)

    def triggerResult(self, result):
        self.clear_results()
        if result.userData == FilterNotConfigured:
            self.openConfigWidget()
            self.iface.invalidateLocatorResults()
            return

        # this should be run in the main thread, i.e. mapCanvas should not be None
        geometry = result.userData
        # geometry.transform(self.transform)
        dbg_info(str(geometry.asWkt()))
        dbg_info(geometry.type())

        try:
            rect = QgsReferencedRectangle(geometry.boundingBox(), self.crs)
            rect.scale(4)
            self.map_canvas.setReferencedExtent(rect)
        except AttributeError:
            # QGIS < 3.10 handling
            from qgis.core import QgsCoordinateTransform, QgsProject
            transform = QgsCoordinateTransform(
                self.crs,
                self.map_canvas.mapSettings().destinationCrs(),
                QgsProject.instance())
            geometry.transform(transform)
            rect = geometry.boundingBox()
            rect.scale(4)
            self.map_canvas.setExtent(rect)

        self.map_canvas.refresh()

        if geometry.type() == QgsWkbTypes.PolygonGeometry:
            nflash = 16
            color1: QColor = self.settings.value('polygon_color')
            color2 = color1
            color1.setAlpha(200)
            color2.setAlpha(100)
            self.map_canvas.flashGeometries(
                [geometry], self.crs, color1, color2, nflash,
                self.settings.value('highlight_duration') / nflash * 1000)
        else:
            self.rubberband.reset(geometry.type())
            self.rubberband.addGeometry(geometry, self.crs)

            self.current_timer = QTimer()
            self.current_timer.timeout.connect(self.clear_results)
            self.current_timer.setSingleShot(True)
            self.current_timer.start(
                self.settings.value('highlight_duration') * 1000)

    def beautify_group(self, group) -> str:
        if self.service.remove_leading_digits:
            group = re.sub('^[0-9]+', '', group)
        if self.service.replace_underscore:
            group = group.replace("_", " ")
        if self.service.break_camelcase:
            group = self.break_camelcase(group)
        return group

    def info(self, msg="", level=Qgis.Info):
        QgsMessageLog.logMessage('{} {}'.format(self.__class__.__name__, msg),
                                 'QgsLocatorFilter', level)

    def dbg_info(self, msg=""):
        if DEBUG:
            self.info(msg)

    @staticmethod
    def break_camelcase(identifier) -> str:
        matches = re.finditer(
            '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)',
            identifier)
        return ' '.join([m.group(0) for m in matches])
Example #26
0
class FinderBox(QComboBox):

    running = False
    to_finish = 0

    search_started = pyqtSignal()
    search_finished = pyqtSignal()

    def __init__(self, finders, iface, parent=None):
        self.iface = iface
        self.mapCanvas = iface.mapCanvas()
        self.marker = None
        self.rubber = QgsRubberBand(self.mapCanvas)
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

        QComboBox.__init__(self, parent)
        self.setEditable(True)
        self.setInsertPolicy(QComboBox.InsertAtTop)
        self.setMinimumHeight(27)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.insertSeparator(0)
        self.lineEdit().returnPressed.connect(self.search)

        self.result_view = QTreeView()
        self.result_view.setHeaderHidden(True)
        self.result_view.setMinimumHeight(300)
        self.result_view.activated.connect(self.itemActivated)
        self.result_view.pressed.connect(self.itemPressed)
        self.setView(self.result_view)

        self.result_model = ResultModel(self)
        self.setModel(self.result_model)

        self.finders = finders
        for finder in list(self.finders.values()):
            finder.result_found.connect(self.result_found)
            finder.limit_reached.connect(self.limit_reached)
            finder.finished.connect(self.finished)

        self.clearButton = QPushButton(self)
        self.clearButton.setIcon(
            QIcon(":/plugins/quickfinder/icons/draft.svg"))
        self.clearButton.setText('')
        self.clearButton.setFlat(True)
        self.clearButton.setCursor(QCursor(Qt.ArrowCursor))
        self.clearButton.setStyleSheet('border: 0px; padding: 0px;')
        self.clearButton.clicked.connect(self.clear)

        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.addStretch()
        layout.addWidget(self.clearButton)
        layout.addSpacing(20)

        button_size = self.clearButton.sizeHint()
        # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
        padding = button_size.width()  # + frameWidth + 1
        self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' %
                                      padding)

    def __del__(self):
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    #clear marker that the lonlat seted
    def clearMarker(self):
        self.mapCanvas.scene().removeItem(self.marker)
        self.marker = None

    def clearSelection(self):
        self.result_model.setSelected(None, self.result_view.palette())
        self.rubber.reset()
        #self.clearMarker()

    def clear(self):
        self.clearSelection()
        self.result_model.clearResults()
        self.lineEdit().setText('')

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.clearSelection()
            self.clearMarker()
        QComboBox.keyPressEvent(self, event)

    def lnglatFinder(self, to_find):
        import re
        m = re.match(r'(?P<lon>-?\d*(.\d+))\s+(?P<lat>-?\d*(.\d+))', to_find)
        if not m:
            return False
        x = float(m.group('lon'))
        y = float(m.group('lat'))
        return self.zoomLnglat(x, y)

    def zoomLnglat(self, lng, lat):
        x, y = lng, lat

        canvas = self.mapCanvas
        currExt = canvas.extent()
        canvasCenter = currExt.center()
        dx = float(x) - canvasCenter.x()
        dy = float(y) - canvasCenter.y()

        xMin = currExt.xMinimum() + dx
        xMax = currExt.xMaximum() + dx
        yMin = currExt.yMinimum() + dy
        yMax = currExt.yMaximum() + dy

        rect = QgsRectangle(xMin, yMin, xMax, yMax)
        canvas.setExtent(rect)
        pt = QgsPointXY(float(x), float(y))
        self.marker = QgsVertexMarker(canvas)
        self.marker.setCenter(pt)
        self.marker.setIconSize(18)
        self.marker.setPenWidth(2)
        self.marker.setIconType(QgsVertexMarker.ICON_CROSS)
        canvas.refresh()
        return True

#    def geocodeFinder(self, to_finder):
#        print(to_finder[:2])
#        if not to_finder[:2] == 'b:':
#            return False
#
#        address = to_finder[2:]
#        url = MySettings().value("baiduUrl")
#        url = url + parse.quote(address)
#
#        response = request.urlopen(url)
#        content = response.read()
#        data = json.loads(content)
#        print(data)
#        lng, lat = (data['result']['location']['lng'], data['result']['location']['lat'])
#        from .cood_trans import bd09_to_wgs84
#        lng, lat = bd09_to_wgs84(lng, lat)
#        print(f'{lng}-{lat}')
#        return self.zoomLnglat(lng, lat)

    def search(self):
        #  self.geocode()
        if self.running:
            return

        to_find = self.lineEdit().text()
        if not to_find or to_find == '':
            return
#        if not (self.lnglatFinder(to_find) or self.geocodeFinder(to_find)):
        if not self.lnglatFinder(to_find):
            self.showPopup()

        self.running = True
        self.search_started.emit()

        self.clearSelection()
        self.result_model.clearResults()
        self.result_model.truncateHistory(MySettings().value("historyLength"))
        self.result_model.setLoading(True)

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        self.finders_to_start = []
        for finder in list(self.finders.values()):
            if finder.activated():
                self.finders_to_start.append(finder)

        bbox = self.mapCanvas.fullExtent()

        while len(self.finders_to_start) > 0:
            finder = self.finders_to_start[0]
            self.finders_to_start.remove(finder)
            self.result_model.addResult(finder.name)
            finder.start(to_find, bbox=bbox)

        # For case there is no finder activated
        self.finished(None)

    def stop(self):
        self.finders_to_start = []
        for finder in list(self.finders.values()):
            if finder.is_running():
                finder.stop()
        self.finished(None)

    def result_found(self, finder, layername, value, geometry, srid):
        self.result_model.addResult(finder.name, layername, value, geometry,
                                    srid)
        self.result_view.expandAll()

    def limit_reached(self, finder, layername):
        self.result_model.addEllipsys(finder.name, layername)

    def finished(self, finder):
        if len(self.finders_to_start) > 0:
            return
        for finder in list(self.finders.values()):
            if finder.is_running():
                return

        self.running = False
        self.search_finished.emit()

        self.result_model.setLoading(False)

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

    def itemActivated(self, index):
        item = self.result_model.itemFromIndex(index)
        self.showItem(item)

    def itemPressed(self, index):
        item = self.result_model.itemFromIndex(index)
        if QApplication.mouseButtons() == Qt.LeftButton:
            self.showItem(item)

    def showItem(self, item):
        if isinstance(item, ResultItem):
            self.result_model.setSelected(item, self.result_view.palette())
            geometry = self.transform_geom(item)
            self.rubber.reset(geometry.type())
            self.rubber.setToGeometry(geometry, None)
            self.zoom_to_rubberband()
            return

        if isinstance(item, GroupItem):
            child = item.child(0)
            if isinstance(child, ResultItem):
                self.result_model.setSelected(item, self.result_view.palette())
                self.rubber.reset(child.geometry.type())
                for i in range(0, item.rowCount()):
                    geometry = self.transform_geom(item.child(i))
                    self.rubber.addGeometry(geometry, None)
                self.zoom_to_rubberband()
            return

        if item.__class__.__name__ == 'QStandardItem':
            self.clearSelection()

    def transform_geom(self, item):
        src_crs = QgsCoordinateReferenceSystem()
        src_crs.createFromSrid(item.srid)
        dest_crs = self.mapCanvas.mapSettings().destinationCrs()
        geom = QgsGeometry(item.geometry)
        geom.transform(
            QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance()))
        return geom

    def zoom_to_rubberband(self):
        geom = self.rubber.asGeometry()
        if geom:
            rect = geom.boundingBox()
            rect.scale(1.5)
            self.mapCanvas.setExtent(rect)
            self.mapCanvas.refresh()
class SelectFeaturesTool(QgsMapTool):
    selection_clicked = pyqtSignal(list)

    def __init__(self, mission_track, canvas):
        QgsMapTool.__init__(self, canvas)
        self.setCursor(Qt.ArrowCursor)
        self.mission_track = mission_track
        self.layer = self.mission_track.get_mission_layer()
        self.rubber_band = None
        self.rubber_band_points = None
        self.selection_polygon = []
        self.indexes_within_list = []
        self.band_finished = True
        self.mCtrl = False
        self.p0, self.p1, self.p2, self.p3 = None, None, None, None
        self.mission_track.mission_changed.connect(self.update_rubber_band)
        self.mission_track.step_removed.connect(self.remove_rubber_band)
        self.wp = self.mission_track.find_waypoints_in_mission()
        self.layer.startEditing()
        self.rubber_band_vs_track_indexes = {}
        self.rubber_band_points = QgsRubberBand(self.canvas(),
                                                QgsWkbTypes.PointGeometry)
        self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubber_band_points.setIconSize(10)
        self.rubber_band_points.setColor(QColor("green"))
        self.rubber_band_vertex_counter = 0

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = True

    def keyReleaseEvent(self, event):
        if event.key() == Qt.Key_Control:
            self.mCtrl = False
        if event.key() == Qt.Key_Escape:
            self.p0, self.p1, self.p2, self.p3 = None, None, None, None
            if self.rubber_band:
                self.rubber_band.reset(True)
            self.close_polygon_band()
            self.band_finished = True
            self.canvas().refresh()
            return

    def canvasPressEvent(self, event):
        if event.button() == Qt.LeftButton:
            point = self.toMapCoordinates(event.pos())
            # check if we have clicked on a vertex
            tolerance = self.calc_tolerance()
            vertex = self.find_vertex_at(event.pos(), tolerance)
            if self.mCtrl and vertex is not None:
                # if we have clicked on a vertex, identify which one
                # check if was already in the selection list
                if vertex not in self.indexes_within_list:
                    # add it
                    self.indexes_within_list.append(vertex)
                    self.update_rubber_band()
                else:
                    # remove it
                    self.indexes_within_list.remove(vertex)
                    self.update_rubber_band()

                self.band_finished = True
            elif vertex is None:
                # if we have not clicked on a vertex and there's no polygon band, start it
                if not len(self.selection_polygon
                           ) and self.band_finished and not self.mCtrl:
                    self.selection_clicked.emit(list())
                    self.band_finished = False
                    self.rubber_band = QgsRubberBand(
                        self.canvas(), QgsWkbTypes.PolygonGeometry)
                    self.rubber_band.setWidth(2)
                    select_green = QColor("green")
                    select_green.setAlpha(128)
                    self.rubber_band.setColor(select_green)

                    if event.button() == Qt.LeftButton:
                        # Left click -> add vertex
                        self.p0 = QgsPointXY(point.x(), point.y())
                        self.selection_polygon.append(self.p0)
                elif len(self.selection_polygon) == 1 and not self.mCtrl:
                    if event.button() == Qt.LeftButton:
                        # Left click -> add vertex
                        self.p2 = QgsPointXY(point.x(), point.y())
                        self.p1 = QgsPointXY(self.p2.x(), self.p0.y())
                        self.p3 = QgsPointXY(self.p0.x(), self.p2.y())
                        self.selection_polygon.append(self.p1)
                        self.selection_polygon.append(self.p2)
                        self.selection_polygon.append(self.p3)
                        self.band_finished = True
                        self.set_selection()
                        self.close_polygon_band()

            self.selection_clicked.emit(self.indexes_within_list)

    def find_vertex_at(self, pos, tolerance):
        """
        get the vertex that is closer to the clicked point


        :param pos: The point that we've clicked
        :param tolerance: The tolerance of pos
        :return: vertex or None
        """
        if len(self.wp) > 0:
            dist_to_vertex = []
            for v in range(0, len(self.wp)):
                a1 = self.toCanvasCoordinates(QgsPointXY(self.wp[v]))
                dist_to_vertex.append(
                    math.sqrt((pos.x() - a1.x())**2 + (pos.y() - a1.y())**2))

            vertex = dist_to_vertex.index(min(dist_to_vertex))
            if min(dist_to_vertex) > tolerance:
                return None
            else:
                return vertex
        else:
            return None

    def calc_tolerance(self):
        """
        Compute the tolerance on canvas

        :return: tolerance
        """
        # 2% of tolerance
        width_tolerance = 0.02 * self.canvas().width()
        height_tolerance = 0.02 * self.canvas().height()
        if width_tolerance < height_tolerance:
            tolerance = width_tolerance
        else:
            tolerance = height_tolerance
        return tolerance

    def canvasMoveEvent(self, event):

        if not self.band_finished and not self.mCtrl:
            self.p2 = self.toMapCoordinates(event.pos())
            self.p1 = QgsPointXY(self.p2.x(), self.p0.y())
            self.p3 = QgsPointXY(self.p0.x(), self.p2.y())

            self.selection_polygon.append(self.p1)
            self.selection_polygon.append(self.p2)
            self.selection_polygon.append(self.p3)
            self.rubber_band.setToGeometry(
                QgsGeometry.fromPolygonXY([self.selection_polygon]), None)
            self.selection_polygon.pop()
            self.selection_polygon.pop()
            self.selection_polygon.pop()

    def set_selection(self):
        """
        Set vertices highlight according to polygon
        """
        # Check which features are within the polygon
        mission_track = self.layer.getFeatures()  # get mission track feature

        for f in mission_track:  # loop although mission layer only has one feature
            vertices_it = f.geometry().vertices()
        polygon_geom = QgsGeometry.fromPolygonXY([self.selection_polygon])
        vertices_within_list = []
        # self.indexes_within_list = []
        vertex_index = 0

        # Highlight them using a point rubber band
        self.rubber_band_vertex_counter = 0
        for v in vertices_it:
            point_geom = QgsGeometry.fromPointXY(QgsPointXY(v.x(), v.y()))
            if point_geom.within(polygon_geom):
                vertices_within_list.append(v)
                if not (vertex_index in self.indexes_within_list
                        ):  # only add if not already present
                    self.indexes_within_list.append(vertex_index)
                    self.rubber_band_points.addPoint(QgsPointXY(v.x(), v.y()))
                    self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1
                    self.rubber_band_vs_track_indexes[
                        vertex_index] = self.rubber_band_vertex_counter - 1
            vertex_index = vertex_index + 1

    def update_rubber_band(self):
        if self.rubber_band_points:
            self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
            self.rubber_band_vs_track_indexes = {}
            self.rubber_band_vertex_counter = 0
        self.wp = self.mission_track.find_waypoints_in_mission()
        if len(self.indexes_within_list) > 0:

            selected_vertices = self.mission_track.find_waypoints_in_mission(
                self.indexes_within_list)
            for v in selected_vertices:
                vertex_index = 0
                for point in self.wp:
                    if v == point:
                        pc = self.toLayerCoordinates(self.layer, QgsPointXY(v))
                        self.rubber_band_points.addPoint(pc)
                        self.rubber_band_vertex_counter = self.rubber_band_vertex_counter + 1
                        self.rubber_band_vs_track_indexes[
                            vertex_index] = self.rubber_band_vertex_counter - 1
                    vertex_index = vertex_index + 1

        self.set_geometry()

    def remove_rubber_band(self, wp):
        # if self.rubber_band_points:
        #    self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
        self.indexes_within_list.remove(wp)
        self.update_rubber_band()

    def set_geometry(self):
        """
        Save rubber band to geometry of the layer
        """
        if self.layer.featureCount() == 0:
            # no feature yet created
            f = QgsFeature()
            if len(self.wp) == 1:
                f.setGeometry(
                    QgsGeometry.fromPointXY(
                        QgsPointXY(self.wp[0].x(), self.wp[0].y())))
            else:
                f.setGeometry(QgsGeometry.fromPolyline(self.wp))
            # self.layer.dataProvider().addFeatures([f])
            self.layer.addFeatures([f])
        else:
            # mission feature present, edit geometry
            feats = self.layer.getFeatures()
            for f in feats:
                if len(self.wp) == 1:
                    self.layer.changeGeometry(
                        f.id(),
                        QgsGeometry.fromPointXY(
                            QgsPointXY(self.wp[0].x(), self.wp[0].y())))
                else:
                    self.layer.changeGeometry(
                        f.id(), QgsGeometry.fromPolyline(self.wp))
        self.layer.commitChanges()
        self.layer.startEditing()

    def close_polygon_band(self):
        self.selection_polygon = []
        if self.rubber_band is not None:
            self.rubber_band.reset()
            self.canvas().scene().removeItem(self.rubber_band)
        self.rubber_band = None

    def close_highlight_band(self):
        self.rubber_band_points.reset()
        self.canvas().scene().removeItem(self.rubber_band_points)
        self.rubber_band_points = None

    def deactivate(self):
        if self.rubber_band:
            self.close_polygon_band()
        if self.rubber_band_points:
            self.close_highlight_band()

        try:
            self.mission_track.mission_changed.disconnect(
                self.update_rubber_band)
            self.mission_track.step_removed.disconnect(self.remove_rubber_band)
        except:
            logger.info("no connected to signal")
        self.layer.commitChanges()
Example #28
0
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": None
            })

    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.setScale(self.layer.xScale * xScale,
                                self.layer.yScale * yScale)
            val = self.layer.rotation + rotation
            if val > 180:
                val = val - 360
            self.iface.mainWindow().findChild(
                QDoubleSpinBox,
                'FreehandRasterGeoreferencer_spinbox').setValue(val)

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

            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 #29
0
class ExtrapolateTool(QgsMapTool):
    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.__canvas = iface.mapCanvas()
        self.__icon_path = ':/plugins/VDLTools/icons/extrapolate_icon.png'
        self.__text = QCoreApplication.translate(
            "VDLTools", "Extrapolate the elevation of a vertex and a "
            "point at the extremity of a line")
        # self.__oldTool = None
        self.__layer = None
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__lastFeatureId = None
        self.__rubber = None
        self.__counter = 0
        self.__confDlg = None
        self.__selectedVertex = None
        self.__elevation = None
        self.__selectedFeature = None
        self.__layerConfig = None

    def icon_path(self):
        """
        To get the icon path
        :return: icon path
        """
        return self.__icon_path

    def text(self):
        """
        To get the menu text
        :return: menu text
        """
        return self.__text

    def setTool(self):
        """
        To set the current tool as this one
        """
        # self.__oldTool = self.__canvas.mapTool()
        self.__canvas.setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__rubber = QgsRubberBand(self.__canvas, QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setIconSize(20)
        self.__updateList()
        self.__canvas.layersChanged.connect(self.__updateList)
        QgsProject.instance().snapSettingsChanged.connect(self.__updateList)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__rubber.reset()
        self.__canvas.layersChanged.disconnect(self.__updateList)
        QgsProject.instance().snapSettingsChanged.disconnect(self.__updateList)
        QgsMapTool.deactivate(self)

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        self.__layer.editingStarted.disconnect(self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        self.__layer.editingStopped.disconnect(self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.__canvas.mapTool == self:
            self.__iface.actionPan().trigger()
        #     self.__canvas.setMapTool(self.__oldTool)

    def removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                self.__layer.editingStopped.disconnect(self.stopEditing)
            else:
                self.__layer.editingStarted.disconnect(self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None\
                and isinstance(layer, QgsVectorLayer)\
                and QGis.fromOldWkbType(layer.wkbType()) == QgsWKBTypes.LineStringZ:

            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    self.__layer.editingStopped.disconnect(self.stopEditing)
                else:
                    self.__layer.editingStarted.disconnect(self.startEditing)
            self.__layer = layer
            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__updateList()
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.__canvas.mapTool == self:
                    self.__iface.actionPan().trigger()
                #    self.__canvas.setMapTool(self.__oldTool)
            return
        self.action().setEnabled(False)
        self.removeLayer()

    def __updateList(self):
        """
        To update the snapping options of the layer
        """
        noUse, enabled, snappingType, unitType, tolerance, avoidIntersection = \
            QgsProject.instance().snapSettingsForLayer(self.__layer.id())
        self.__layerConfig = QgsSnappingUtils.LayerConfig(
            self.__layer, QgsPointLocator.Vertex, tolerance, unitType)
        if not enabled or tolerance == 0:
            self.__iface.messageBar().pushMessage(
                QCoreApplication.translate("VDLTools", "Error"),
                QCoreApplication.translate(
                    "VDLTools", "This layer has no snapping options"),
                level=QgsMessageBar.CRITICAL)

    def canvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """
        if not self.__isEditing:
            f = Finder.findClosestFeatureAt(event.mapPoint(),
                                            self.__layerConfig, self)

            if f is not None and self.__lastFeatureId != f.id():
                self.__lastFeatureId = f.id()
                self.__layer.setSelectedFeatures([f.id()])
                if self.__counter > 2:
                    self.__rubber.reset()
                    geom = f.geometry()
                    index = geom.closestVertex(event.mapPoint())[1]
                    line_v2, curved = GeometryV2.asLineV2(geom)
                    num_p = line_v2.numPoints()
                    if num_p > 2 and (index == 0 or index == (num_p - 1)):
                        self.__rubber.setIcon(4)
                        self.__rubber.setToGeometry(
                            QgsGeometry(line_v2.pointN(index)), None)
                        self.__counter = 0
                else:
                    self.__counter += 1
            if f is None:
                self.__layer.removeSelection()
                self.__rubber.reset()
                self.__lastFeatureId = None

    def canvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        found_features = self.__layer.selectedFeatures()
        if len(found_features) > 0:
            if len(found_features) < 1:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools",
                                               "One feature at a time"),
                    level=QgsMessageBar.INFO)
                return
            geom = found_features[0].geometry()
            self.__selectedVertex = geom.closestVertex(event.mapPoint())[1]
            line_v2, curved = GeometryV2.asLineV2(geom)
            num_p = line_v2.numPoints()
            if num_p > 2 and (self.__selectedVertex == 0
                              or self.__selectedVertex == (num_p - 1)):
                pt = line_v2.pointN(self.__selectedVertex)
                if self.__selectedVertex == 0:
                    pt0 = line_v2.pointN(2)
                    pt1 = line_v2.pointN(1)
                else:
                    pt0 = line_v2.pointN(num_p - 3)
                    pt1 = line_v2.pointN(num_p - 2)
                big_d = Finder.sqrDistForPoints(pt0, pt1)
                small_d = Finder.sqrDistForPoints(pt1, pt)
                if small_d < (big_d / 4):
                    self.__isEditing = True
                    self.__selectedFeature = found_features[0]
                    self.__elevation = pt0.z() + (1 + small_d / big_d) * (
                        pt1.z() - pt0.z())
                    if pt.z() is not None and pt.z() != 0:
                        self.__confDlg = ExtrapolateConfirmDialog(
                            pt.z(), self.__elevation)
                        self.__confDlg.okButton().clicked.connect(
                            self.__onEditOk)
                        self.__confDlg.cancelButton().clicked.connect(
                            self.__onEditCancel)
                        self.__confDlg.show()
                    else:
                        self.__edit()
                else:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "The segment is too big"),
                        level=QgsMessageBar.INFO)

    def __onEditOk(self):
        """
        When the Ok button in Extrapolate Confirm Dialog is pushed
        """
        self.__confDlg.close()
        self.__edit()

    def __onEditCancel(self):
        """
        When the Cancel button in Extrapolate Confirm Dialog is pushed
        """
        self.__confDlg.close()
        self.__rubber.reset()
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__selectedVertex = None
        self.__isEditing = False

    def __edit(self):
        """
        To add the new extrapolate elevation
        """
        line_v2, curved = GeometryV2.asLineV2(
            self.__selectedFeature.geometry())
        line_v2.setZAt(self.__selectedVertex, self.__elevation)
        self.__layer.changeGeometry(self.__selectedFeature.id(),
                                    QgsGeometry(line_v2))
        self.__layer.removeSelection()
        self.__rubber.reset()
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__selectedVertex = None
        self.__isEditing = False
Example #30
0
class VideoWidget(QVideoWidget):

    def __init__(self, parent=None):
        ''' Constructor '''
        super().__init__(parent)
        self.surface = VideoWidgetSurface(self)
        self.setAttribute(Qt.WA_OpaquePaintEvent)

        self.Tracking_Video_RubberBand = QRubberBand(QRubberBand.Rectangle, self)
        self.Censure_RubberBand = QRubberBand(QRubberBand.Rectangle, self)

        color_blue = QColor(Qt.blue)
        color_black = QColor(Qt.black)
        color_amber = QColor(252, 215, 108)

        pal_blue = QPalette()
        pal_blue.setBrush(QPalette.Highlight, QBrush(color_blue))
        self.Tracking_Video_RubberBand.setPalette(pal_blue)

        pal_black = QPalette()
        pal_black.setBrush(QPalette.Highlight, QBrush(color_black))
        self.Censure_RubberBand.setPalette(pal_black)

        self._interaction = InteractionState()
        self._filterSatate = FilterState()

        self._isinit = False
        self._MGRS = False
        self.gt = None

        self.drawCesure = []
        self.poly_coordinates, self.drawPtPos, self.drawLines, self.drawMeasureDistance, self.drawMeasureArea, self.drawPolygon = [], [], [], [], [], []
        # Draw Polygon Canvas Rubberband
        self.poly_Canvas_RubberBand = QgsRubberBand(
            iface.mapCanvas(), True)  # Polygon type
        # set rubber band style
        self.poly_Canvas_RubberBand.setColor(color_amber)
        self.poly_Canvas_RubberBand.setWidth(3)

        # Tracking Canvas Rubberband
        self.Track_Canvas_RubberBand = QgsRubberBand(
            iface.mapCanvas(), QgsWkbTypes.LineGeometry)
        # set rubber band style
        self.Track_Canvas_RubberBand.setColor(color_blue)
        self.Track_Canvas_RubberBand.setWidth(5)

        # Cursor Canvas Rubberband
        self.Cursor_Canvas_RubberBand = QgsRubberBand(
            iface.mapCanvas(), QgsWkbTypes.PointGeometry)
        self.Cursor_Canvas_RubberBand.setWidth(4)
        self.Cursor_Canvas_RubberBand.setColor(QColor(255, 100, 100, 250))
        self.Cursor_Canvas_RubberBand.setIcon(QgsRubberBand.ICON_FULL_DIAMOND)

        self.parent = parent.parent()

        palette = self.palette()
        palette.setColor(QPalette.Background, Qt.transparent)
        self.setPalette(palette)

        self.origin, self.dragPos = QPoint(), QPoint()
        self.tapTimer = QBasicTimer()
        self.brush = QBrush(color_black)
        self.blue_Pen = QPen(color_blue, 3)

    def removeLastLine(self):
        ''' Remove Last Line Objects '''
        if self.drawLines:
            try:
                if self.drawLines[-1][3] == "mouseMoveEvent":
                    del self.drawLines[-1]  # Remove mouseMoveEvent element
            except Exception:
                None
            for pt in range(len(self.drawLines) - 1, -1, -1):
                del self.drawLines[pt]
                try:
                    if self.drawLines[pt - 1][0] is None:
                        break
                except Exception:
                    None
            self.UpdateSurface()
            AddDrawLineOnMap(self.drawLines)
        return

    def removeLastSegmentLine(self):
        ''' Remove Last Segment Line Objects '''
        try:
            if self.drawLines[-1][3] == "mouseMoveEvent":
                del self.drawLines[-1]  # Remove mouseMoveEvent element
        except Exception:
            None
        if self.drawLines:
            if self.drawLines[-1][0] is None:
                del self.drawLines[-1]

            del self.drawLines[-1]
            self.UpdateSurface()
            AddDrawLineOnMap(self.drawLines)
        return

    def removeAllLines(self):
        ''' Resets Line List '''
        if self.drawLines:
            self.drawLines = []
            self.UpdateSurface()
            # Clear all Layer
            RemoveAllDrawLineOnMap()

    def ResetDrawMeasureDistance(self):
        ''' Resets Measure Distance List '''
        self.drawMeasureDistance = []

    def ResetDrawMeasureArea(self):
        ''' Resets Measure Area List '''
        self.drawMeasureArea = []

    def removeAllCensure(self):
        ''' Remove All Censure Objects '''
        if self.drawCesure:
            self.drawCesure = []
            self.UpdateSurface()

    def removeLastCensured(self):
        ''' Remove Last Censure Objects '''
        if self.drawCesure:
            del self.drawCesure[-1]
            self.UpdateSurface()

    def removeLastPoint(self):
        ''' Remove All Point Drawer Objects '''
        if self.drawPtPos:
            del self.drawPtPos[-1]
            self.UpdateSurface()
            RemoveLastDrawPointOnMap()
        return

    def removeAllPoint(self):
        ''' Remove All Point Drawer Objects '''
        if self.drawPtPos:
            self.drawPtPos = []
            self.UpdateSurface()
            # Clear all Layer
            RemoveAllDrawPointOnMap()
        return

    def removeAllPolygon(self):
        ''' Remove All Polygon Drawer Objects '''
        if self.drawPolygon:
            self.drawPolygon = []
            self.UpdateSurface()
            # Clear all Layer
            RemoveAllDrawPolygonOnMap()

    def removeLastPolygon(self):
        ''' Remove Last Polygon Drawer Objects '''
        if self.drawPolygon:
            try:
                if self.drawPolygon[-1][3] == "mouseMoveEvent":
                    del self.drawPolygon[-1]  # Remove mouseMoveEvent element
            except Exception:
                None
            for pt in range(len(self.drawPolygon) - 1, -1, -1):
                del self.drawPolygon[pt]
                try:
                    if self.drawPolygon[pt - 1][0] is None:
                        break
                except Exception:
                    None

            self.UpdateSurface()
            # remove last index layer
            RemoveLastDrawPolygonOnMap()

    def keyPressEvent(self, event):
        '''Exit fullscreen
        :type event: QKeyEvent
        :param event:
        :return:
        '''
        if event.key() == Qt.Key_Escape and self.isFullScreen():
            self.setFullScreen(False)
            event.accept()
        elif event.key() == Qt.Key_Enter and event.modifiers() & Qt.Key_Alt:
            self.setFullScreen(not self.isFullScreen())
            event.accept()
        else:
            super().keyPressEvent(event)

    def mouseDoubleClickEvent(self, event):
        """
         Mouse double click event
        :type event: QMouseEvent
        :param event:
        :return:
        """
        if GetImageHeight() == 0:
            return

        if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)):
            return

        if self.gt is not None and self._interaction.lineDrawer:
            self.drawLines.append([None, None, None])
            return

        if self.gt is not None and self._interaction.measureDistance:
            self.drawMeasureDistance.append([None, None, None])
            return

        if self.gt is not None and self._interaction.measureArea:
            self.drawMeasureArea.append([None, None, None])
            return

        if self.gt is not None and self._interaction.polygonDrawer:

            ok = AddDrawPolygonOnMap(self.poly_coordinates)
            # Prevent invalid geometry (Polygon with 2 points)
            if not ok:
                return

            self.drawPolygon.append([None, None, None])

            # Empty RubberBand
            for _ in range(self.poly_Canvas_RubberBand.numberOfVertices()):
                self.poly_Canvas_RubberBand.removeLastPoint()
            # Empty List
            self.poly_coordinates = []
            return

        self.UpdateSurface()
        self.setFullScreen(not self.isFullScreen())
        event.accept()

    def videoSurface(self):
        ''' Return video Surface '''
        return self.surface

    def UpdateSurface(self):
        ''' Update Video Surface only is is stopped or paused '''
        if self.parent.playerState in (QMediaPlayer.StoppedState,
                                       QMediaPlayer.PausedState):
            self.update()
        QApplication.processEvents()

    def sizeHint(self):
        ''' This property holds the recommended size for the widget '''
        return self.surface.surfaceFormat().sizeHint()

    def currentFrame(self):
        ''' Return current frame QImage '''
        return self.surface.image

    def SetInvertColor(self, value):
        '''Set Invert color filter
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.invertColorFilter = value

    def SetObjectTracking(self, value):
        '''Set Object Tracking
        @type value: bool
        @param value:
        @return:
        '''
        self._interaction.objectTracking = value

    def SetMeasureDistance(self, value):
        '''Set measure Distance
        @type value: bool
        @param value:
        @return:
        '''
        self._interaction.measureDistance = value

    def SetMeasureArea(self, value):
        '''Set measure Area
        @type value: bool
        @param value:
        @return:
        '''
        self._interaction.measureArea = value

    def SetHandDraw(self, value):
        '''Set Hand Draw
        @type value: bool
        @param value:
        @return:
        '''
        self._interaction.HandDraw = value

    def SetCensure(self, value):
        '''Set Censure Video Parts
        @type value: bool
        @param value:
        @return:
        '''
        self._interaction.censure = value

    def SetMGRS(self, value):
        '''Set MGRS Cursor Coordinates
        @type value: bool
        @param value:
        @return:
        '''
        self._MGRS = value

    def SetGray(self, value):
        '''Set gray scale
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.grayColorFilter = value

    def SetMirrorH(self, value):
        '''Set Horizontal Mirror
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.MirroredHFilter = value

    def SetNDVI(self, value):
        '''Set NDVI
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.NDVI = value

    def SetEdgeDetection(self, value):
        '''Set Canny Edge filter
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.edgeDetectionFilter = value

    def SetAutoContrastFilter(self, value):
        '''Set Automatic Contrast filter
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.contrastFilter = value

    def SetMonoFilter(self, value):
        '''Set mono filter
        @type value: bool
        @param value:
        @return:
        '''
        self._filterSatate.monoFilter = value

    def RestoreFilters(self):
        ''' Remove and restore all video filters '''
        self._filterSatate.clear()

    def RestoreDrawer(self):
        ''' Remove and restore all Drawer Options '''
        self._interaction.clear()
        # Magnifier Glass
        self.dragPos = QPoint()
        self.tapTimer.stop()

    def RemoveCanvasRubberbands(self):
        ''' Remove Canvas Rubberbands '''
        self.poly_Canvas_RubberBand.reset()
        self.Track_Canvas_RubberBand.reset(QgsWkbTypes.LineGeometry)
        self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry)

    def paintEvent(self, event):
        """
        @type event: QPaintEvent
        @param event:
        @return:
        """
        if not self.surface.isActive():
            return

        self.painter = QPainter(self)
        self.painter.setRenderHint(QPainter.HighQualityAntialiasing)

        region = event.region()
        self.painter.fillRect(region.boundingRect(), self.brush)  # Background painter color

        try:
            self.surface.paint(self.painter)
            SetImageSize(self.currentFrame().width(),
                         self.currentFrame().height())
        except Exception:
            None

        # Prevent draw on video if not started or finished
        if self.parent.player.position() == 0:
            self.painter.end()
            return

        self.gt = GetGCPGeoTransform()

        # Draw On Video
        draw.drawOnVideo(self.drawPtPos, self.drawLines, self.drawPolygon,
                         self.drawMeasureDistance, self.drawMeasureArea, self.drawCesure, self.painter, self.surface, self.gt)

        # Draw On Video Object tracking test
        if self._interaction.objectTracking and self._isinit:
            frame = convertQImageToMat(self.currentFrame())
            offset = self.surface.videoRect()
            # Update tracker
            result = resize(frame, (offset.width(), offset.height()))
            ok, bbox = self.tracker.update(result)
            # Draw bounding box
            if ok:
                # check negative values
                x = bbox[0] + offset.x()
                y = bbox[1] + offset.y()
                if vut.IsPointOnScreen(x, y, self.surface):
                    self.painter.setPen(self.blue_Pen)
                    self.painter.drawRect(x, y, bbox[2], bbox[3])

                    # Get Track object center
                    xc = x + (bbox[2] / 2)
                    yc = y + (bbox[3] / 2)
                    p = QPoint(xc, yc)
                    Longitude, Latitude, _ = vut.GetPointCommonCoords(
                        p, self.surface)
                    # Draw Rubber Band on canvas
                    self.Track_Canvas_RubberBand.addPoint(QgsPointXY(Longitude, Latitude))

            else:
                self._isinit = False
                del self.tracker

        # Magnifier Glass
        if self._interaction.magnifier and not self.dragPos.isNull():
            draw.drawMagnifierOnVideo(self, self.dragPos, self.currentFrame(), self.painter)

        # Stamp On Video
        if self._interaction.stamp:
            draw.drawStampOnVideo(self, self.painter)

        self.painter.end()
        return

    def resizeEvent(self, _):
        """
        @type _: QMouseEvent
        @param _:
        @return:
        """
        self.surface.updateVideoRect()
        self.update()
        # Magnifier Glass
        if self._interaction.magnifier and not self.dragPos.isNull():
            draw.drawMagnifierOnVideo(self, self.dragPos, self.currentFrame(), self.painter)
        # QApplication.processEvents()

    def AddMoveEventValue(self, values, Longitude, Latitude, Altitude):
        """
        Remove and Add move value for fluid drawing

        @type values: list
        @param values: Points list

        @type Longitude: float
        @param Longitude: Longitude value

        @type Latitude: float
        @param Latitude: Latitude value

        @type Altitude: float
        @param Altitude: Altitude value

        """
        for idx, pt in enumerate(values):
            if pt[-1] == "mouseMoveEvent":
                del values[idx]
        values.append([Longitude, Latitude, Altitude, "mouseMoveEvent"])

        self.UpdateSurface()

    def mouseMoveEvent(self, event):
        """
        @type event: QMouseEvent
        @param event:
        @return:
        """
        # Magnifier mouseMoveEvent
        # Magnifier can move on black screen for show image borders
        if self._interaction.magnifier:
            if event.buttons():
                self.dragPos = event.pos()
                self.UpdateSurface()

        # check if the point  is on picture (not in black borders)
        if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)):
            self.setCursor(QCursor(Qt.ArrowCursor))
            self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry)
            return

        # Prevent draw on video if not started or finished
        if self.parent.player.position() == 0:
            return

        # Mouser cursor drawing
        if self._interaction.pointDrawer or self._interaction.polygonDrawer or self._interaction.lineDrawer or self._interaction.measureDistance or self._interaction.measureArea or self._interaction.censure or self._interaction.objectTracking:
            self.setCursor(QCursor(Qt.CrossCursor))

        # Cursor Coordinates
        if self.gt is not None:

            Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                event, self.surface)

            tr = QgsCoordinateTransform( QgsCoordinateReferenceSystem( 'EPSG:4326' ), iface.mapCanvas().mapSettings().destinationCrs(), QgsProject.instance().transformContext() )
            mapPt = tr.transform( QgsPointXY(Longitude, Latitude) )
            
            vertices = self.Cursor_Canvas_RubberBand.numberOfVertices()
            if vertices > 0:
                self.Cursor_Canvas_RubberBand.removePoint(0, True, 0)
                self.Cursor_Canvas_RubberBand.movePoint( mapPt, 0)
            else:
                self.Cursor_Canvas_RubberBand.addPoint( mapPt )

            if self._MGRS:
                try:
                    mgrsCoords = mgrs.toMgrs(Latitude, Longitude)
                except Exception:
                    mgrsCoords = ""

                txt = "<span style='font-size:9pt; font-weight:normal;'>" + \
                    ("%s" % mgrsCoords) + "</span>"

            else:

                txt = "<span style='font-size:10pt; font-weight:bold;'>Lon : </span>"
                txt += "<span style='font-size:9pt; font-weight:normal;'>" + \
                    ("%.3f" % Longitude) + "</span>"
                txt += "<span style='font-size:10pt; font-weight:bold;'> Lat : </span>"
                txt += "<span style='font-size:9pt; font-weight:normal;'>" + \
                    ("%.3f" % Latitude) + "</span>"

                if hasElevationModel():
                    txt += "<span style='font-size:10pt; font-weight:bold;'> Alt : </span>"
                    txt += "<span style='font-size:9pt; font-weight:normal;'>" + \
                        ("%.0f" % Altitude) + "</span>"
                else:
                    txt += "<span style='font-size:10pt; font-weight:bold;'> Alt : </span>"
                    txt += "<span style='font-size:9pt; font-weight:normal;'>-</span>"

            self.parent.lb_cursor_coord.setText(txt)

            # Polygon drawer mouseMoveEvent
            if self._interaction.polygonDrawer:
                self.AddMoveEventValue(self.drawPolygon, Longitude, Latitude, Altitude)

            # Line drawer mouseMoveEvent
            if self._interaction.lineDrawer:
                self.AddMoveEventValue(self.drawLines, Longitude, Latitude, Altitude)

            # Measure Distance drawer mouseMoveEvent
            if self._interaction.measureDistance and self.drawMeasureDistance:
                self.AddMoveEventValue(self.drawMeasureDistance, Longitude, Latitude, Altitude)

            # Measure Area drawer mouseMoveEvent
            if self._interaction.measureArea and self.drawMeasureArea:
                self.AddMoveEventValue(self.drawMeasureArea, Longitude, Latitude, Altitude)

        else:
            self.parent.lb_cursor_coord.setText("<span style='font-size:10pt; font-weight:bold;'>Lon :</span>" +
                                                "<span style='font-size:9pt; font-weight:normal;'>-</span>" +
                                                "<span style='font-size:10pt; font-weight:bold;'> Lat :</span>" +
                                                "<span style='font-size:9pt; font-weight:normal;'>-</span>" +
                                                "<span style='font-size:10pt; font-weight:bold;'> Alt :</span>" +
                                                "<span style='font-size:9pt; font-weight:normal;'>-</span>")

        if not event.buttons():
            return

        # Object tracking rubberband
        if not self.Tracking_Video_RubberBand.isHidden():
            self.Tracking_Video_RubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())

        # Censure rubberband
        if not self.Censure_RubberBand.isHidden():
            self.Censure_RubberBand.setGeometry(
                QRect(self.origin, event.pos()).normalized())

    def timerEvent(self, _):
        """ Time Event (Magnifier method)"""
        if not self._interaction.magnifier:
            self.activateMagnifier()

    def mousePressEvent(self, event):
        """
        @type event: QMouseEvent
        @param event:
        @return:
        """
        if GetImageHeight() == 0:
            return

        # Prevent draw on video if not started or finished
        if self.parent.player.position() == 0:
            return

        if event.button() == Qt.LeftButton:

            # Magnifier Glass
            if self._interaction.magnifier:
                self.dragPos = event.pos()
                self.tapTimer.stop()
                self.tapTimer.start(10, self)

            if(not vut.IsPointOnScreen(event.x(), event.y(), self.surface)):
                return

            # point drawer
            if self.gt is not None and self._interaction.pointDrawer:
                Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                    event, self.surface)

                pointIndex = len(self.drawPtPos) + 1
                AddDrawPointOnMap(pointIndex, Longitude,
                                  Latitude, Altitude)

                self.drawPtPos.append([Longitude, Latitude, Altitude])

            # polygon drawer
            if self.gt is not None and self._interaction.polygonDrawer:
                Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                    event, self.surface)
                self.poly_Canvas_RubberBand.addPoint(QgsPointXY(Longitude, Latitude))
                self.poly_coordinates.extend(QgsPointXY(Longitude, Latitude))
                self.drawPolygon.append([Longitude, Latitude, Altitude])

            # line drawer
            if self.gt is not None and self._interaction.lineDrawer:
                Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                    event, self.surface)

                self.drawLines.append([Longitude, Latitude, Altitude])

                AddDrawLineOnMap(self.drawLines)

            self.origin = event.pos()
            # Object Tracking Interaction
            if self._interaction.objectTracking:
                self.Tracking_Video_RubberBand.setGeometry(
                    QRect(self.origin, QSize()))
                self.Tracking_Video_RubberBand.show()

            # Censure Interaction
            if self._interaction.censure:
                self.Censure_RubberBand.setGeometry(
                    QRect(self.origin, QSize()))
                self.Censure_RubberBand.show()

            # Measure Distance drawer
            if self.gt is not None and self._interaction.measureDistance:
                Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                    event, self.surface)
                self.drawMeasureDistance.append([Longitude, Latitude, Altitude])

            # Measure Distance drawer
            if self.gt is not None and self._interaction.measureArea:
                Longitude, Latitude, Altitude = vut.GetPointCommonCoords(
                    event, self.surface)
                self.drawMeasureArea.append([Longitude, Latitude, Altitude])

            # if not called, the paint event is not triggered.
            self.UpdateSurface()

    def activateMagnifier(self):
        """ Activate Magnifier Glass """
        self.tapTimer.stop()
        self.UpdateSurface()

    def SetMagnifier(self, value):
        """Set Magnifier Glass
        @type value: bool
        @param value:
        """
        self._interaction.magnifier = value
        # We avoid that the second time we activate the tool, save the previous position.
        # Always keep the same behavior of the tool
        if not value:
            self.dragPos = QPoint()
            self.tapTimer.stop()

    def SetStamp(self, value):
        """Set Stamp
        @type value: bool
        @param value:
        """
        self._interaction.stamp = value

    def SetPointDrawer(self, value):
        """Set Point Drawer
        @type value: bool
        @param value:
        """
        self._interaction.pointDrawer = value

    def SetLineDrawer(self, value):
        """Set Line Drawer
        @type value: bool
        @param value:
        """
        self._interaction.lineDrawer = value

    def SetPolygonDrawer(self, value):
        """Set Polygon Drawer
        @type value: bool
        @param value:
        """
        self._interaction.polygonDrawer = value

    def mouseReleaseEvent(self, _):
        """
        @type event: QMouseEvent
        @param event:
        @return:
        """
        # Prevent draw on video if not started or finished
        if self.parent.player.position() == 0:
            return

        # Censure Draw Interaction
        if self._interaction.censure:
            geom = self.Censure_RubberBand.geometry()
            self.Censure_RubberBand.hide()
            self.drawCesure.append([geom])

        # Object Tracking Interaction
        if self._interaction.objectTracking:
            geom = self.Tracking_Video_RubberBand.geometry()
            offset = self.surface.videoRect()
            bbox = (geom.x() - offset.x(), geom.y() - offset.y(), geom.width(), geom.height())
            img = self.currentFrame()
            frame = convertQImageToMat(img)
            # Remo rubberband on canvas and video
            self.Tracking_Video_RubberBand.hide()
            self.Track_Canvas_RubberBand.reset()

            self.tracker = TrackerMOSSE_create()
            result = resize(frame, (offset.width(), offset.height()))

            try:
                ok = self.tracker.init(result, bbox)
            except Exception:
                return
            if ok:
                self._isinit = True
                # Get Traker center
                xc = bbox[0] + (geom.width() / 2)
                yc = bbox[1] + (geom.height() / 2)
                p = QPoint(xc, yc)
                Longitude, Latitude, _ = vut.GetPointCommonCoords(
                    p, self.surface)
                # Draw Rubber Band on canvas
                self.Track_Canvas_RubberBand.addPoint(QgsPointXY(Longitude, Latitude))
            else:
                self._isinit = False

    def leaveEvent(self, _):
        """
        @type _: QEvent
        @param _:
        @return:
        """
        # Remove coordinates label value
        self.parent.lb_cursor_coord.setText("")
        # Change cursor
        self.setCursor(QCursor(Qt.ArrowCursor))
        # Reset mouse rubberband
        self.Cursor_Canvas_RubberBand.reset(QgsWkbTypes.PointGeometry)
class TaskingDockWidget(BASE, WIDGET):
    def __init__(
        self,
        parent=None,
    ):
        super().__init__(parent=parent)

        self.setupUi(self)

        self.rect = None
        self.prev_map_tool = None

        self.btnMapTool.setIcon(TASKING_ICON)
        self.btnMapTool.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        self.footprint = QgsRubberBand(iface.mapCanvas(),
                                       QgsWkbTypes.PolygonGeometry)
        self.footprint.setStrokeColor(PLANET_COLOR)
        self.footprint.setFillColor(QColor(204, 235, 239, 100))
        self.footprint.setWidth(2)
        self.marker = QgsRubberBand(iface.mapCanvas(),
                                    QgsWkbTypes.PointGeometry)
        self.marker.setIcon(QgsRubberBand.ICON_SVG)
        self.marker.setSvgIcon(SVG_ICON, QPoint(-15, -30))

        self.map_tool = AOICaptureMapTool(iface.mapCanvas())
        self.map_tool.aoi_captured.connect(self.aoi_captured)
        self.btnMapTool.toggled.connect(self._set_map_tool)
        iface.mapCanvas().mapToolSet.connect(self._map_tool_set)

        self.textBrowserPoint.setHtml("No point selected")
        self.btnOpenDashboard.setEnabled(False)

        self.btnOpenDashboard.clicked.connect(self._open_tasking_dashboard)
        self.btnCancel.clicked.connect(self.cancel_clicked)

        self.visibilityChanged.connect(self.visibility_changed)

        self.textBrowserPoint.viewport().setAutoFillBackground(False)

    def aoi_captured(self, rect, pt):
        self.pt = pt
        self.rect = rect
        self.footprint.setToGeometry(QgsGeometry.fromRect(rect))
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(),
            QgsProject.instance(),
        )
        transformed = transform.transform(pt)
        self.marker.setToGeometry(QgsGeometry.fromPointXY(transformed))
        self._set_map_tool(False)
        text = f"""
                <p><b>Selected Point Coordinates</b></p>
                <p align="center">Latitude : {pt.x():.4f}</p>
                <p align="center">Longitude : {pt.y():.4f}</p>
                """
        self.textBrowserPoint.setHtml(text)
        self.btnCancel.setEnabled(True)
        self.btnOpenDashboard.setEnabled(True)

    def cancel_clicked(self):
        self.footprint.reset(QgsWkbTypes.PolygonGeometry)
        self.marker.reset(QgsWkbTypes.PointGeometry)
        self.btnOpenDashboard.setEnabled(False)
        self.textBrowserPoint.setHtml("")
        self.btnCancel.setEnabled(False)
        self._set_map_tool(False)

    def _set_map_tool(self, checked):
        if checked:
            self.prev_map_tool = iface.mapCanvas().mapTool()
            iface.mapCanvas().setMapTool(self.map_tool)
        else:
            if self.prev_map_tool is not None:
                iface.mapCanvas().setMapTool(self.prev_map_tool)

    def _map_tool_set(self, new, old):
        if new != self.map_tool:
            self.btnMapTool.blockSignals(True)
            self.btnMapTool.setChecked(False)
            self.btnMapTool.blockSignals(False)

    def visibility_changed(self, visible):
        if not visible:
            self.cancel_clicked()

    def _open_tasking_dashboard(self):
        dialog = WarningDialog(self.pt)
        dialog.exec()
class RectangleTemplateWidget(QWidget, Ui_RectangleTemplateWidget):

    def __init__(self, canvas, msglog, current_missiontrack, parent=None):
        super(RectangleTemplateWidget, self).__init__(parent)
        self.setupUi(self)
        self.canvas = canvas
        self.msglog = msglog
        self.current_missiontrack = current_missiontrack
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.rubber_band = None
        self.rubber_band_points = None
        self.initial_point = None

        self.template_type = 'rectangle_template'
        self.wp = list()
        self.mission = None
        self.distance = QgsDistanceArea()
        self.distance.setSourceCrs(QgsCoordinateReferenceSystem(4326), QgsProject.instance().transformContext())
        self.distance.setEllipsoid('WGS84')

        self.wp_list = []

        self.rubber_band = QgsRubberBand(self.canvas, QgsWkbTypes.LineGeometry)
        self.rubber_band.setWidth(2)
        self.rubber_band.setColor(QColor("green"))

        self.rubber_band_points = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry)
        self.rubber_band_points.setColor(QColor("green"))
        self.rubber_band_points.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.rubber_band_points.setIconSize(10)

        self.initial_point = QgsRubberBand(self.canvas, QgsWkbTypes.PointGeometry)
        self.initial_point.setColor(QColor("red"))
        self.initial_point.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.initial_point.setIconSize(10)

        self.onTarget = False
        self.area_points = None
        self.missionAreaDefined = False

        # Get the tools
        self.rectBy3Points_tool = RectBy3PointsTool(self.canvas)
        self.rectByFixedExtentTool = RectByFixedExtentTool(self.canvas, 0.0, 0.0)
        self.rect_from_center_tool = RectFromCenterTool(self.canvas)
        self.rect_from_center_fixed_tool = RectFromCenterFixedTool(self.canvas, 0.0, 0.0)

        self.drawRectangleButton.clicked.connect(self.draw_mission_area)
        self.centerOnTargetButton.clicked.connect(self.draw_mission_area)
        self.rectBy3Points_tool.msgbar.connect(self.pass_message_bar)

        self.depthButton.setAutoExclusive(False)
        self.altitudeButton.setAutoExclusive(False)

        self.depthButton.setChecked(True)
        self.altitudeButton.setChecked(False)

        self.alongTrackLabel.setEnabled(False)
        self.acrossTrackLabel.setEnabled(False)
        self.alongTLength.setEnabled(False)
        self.acrossTLength.setEnabled(False)

        self.fixedExtent.toggled.connect(self.fixed_extend_toggled)
        self.depthButton.toggled.connect(self.depth_toggled)
        self.altitudeButton.toggled.connect(self.altitude_toggled)

        self.initial_point_comboBox.currentIndexChanged.connect(self.change_initial_point)

    def get_template_mission(self):
        return self.mission

    def fixed_extend_toggled(self):
        is_checked = self.fixedExtent.isChecked()
        self.alongTrackLabel.setEnabled(is_checked)
        self.acrossTrackLabel.setEnabled(is_checked)
        self.alongTLength.setEnabled(is_checked)
        self.acrossTLength.setEnabled(is_checked)

    def depth_toggled(self):
        if self.depthButton.isChecked():
            self.altitudeButton.setChecked(False)

    def altitude_toggled(self):
        if self.altitudeButton.isChecked():
            self.depthButton.setChecked(False)

    def pass_message_bar(self, msg):
        self.msglog.logMessage("")
        self.msglog.logMessage(msg, "Lawn Mower", 0)

    def draw_mission_area(self):
        self.onTarget = False
        sender = self.sender().objectName()
        if not self.missionAreaDefined:
            if self.automaticExtent.isChecked():
                if sender == self.drawRectangleButton.objectName():
                    self.msglog.logMessage("Click starting point", "Lawn Mower", 0)
                    # Draw mission area
                    self.rectBy3Points_tool = RectBy3PointsTool(self.canvas)
                    self.canvas.setMapTool(self.rectBy3Points_tool)
                    self.rectBy3Points_tool.msgbar.connect(self.pass_message_bar)
                    self.rectBy3Points_tool.rb_reset_signal.connect(self.clear_initial_point)
                    self.rectBy3Points_tool.rbFinished.connect(self.create_mission_area)
                elif sender == self.centerOnTargetButton.objectName():
                    self.onTarget = True
                    self.msglog.logMessage("Click center point", "Lawn Mower", 0)
                    # Draw mission area
                    self.rect_from_center_tool = RectFromCenterTool(self.canvas)
                    self.canvas.setMapTool(self.rect_from_center_tool)
                    self.rect_from_center_tool.msgbar.connect(self.pass_message_bar)
                    self.rect_from_center_tool.rb_reset_signal.connect(self.clear_initial_point)
                    self.rect_from_center_tool.rbFinished.connect(self.create_mission_area)
                self.missionAreaDefined = True
                return

            elif self.fixedExtent.isChecked():
                if self.alongTLength.value() != 0.0 and self.acrossTLength.value() != 0.0:
                    if sender == self.drawRectangleButton.objectName():
                        self.msglog.logMessage("Click starting point", "Lawn Mower", 0)

                        self.rectByFixedExtentTool = RectByFixedExtentTool(self.canvas,
                                                                           self.alongTLength.value(),
                                                                           self.acrossTLength.value())
                        self.canvas.setMapTool(self.rectByFixedExtentTool)
                        self.rectByFixedExtentTool.msgbar.connect(self.pass_message_bar)
                        self.rectByFixedExtentTool.rb_reset_signal.connect(self.clear_initial_point)
                        self.rectByFixedExtentTool.rbFinished.connect(self.create_mission_area)
                        self.missionAreaDefined = True
                    elif sender == self.centerOnTargetButton.objectName():
                        self.onTarget = True
                        self.msglog.logMessage("Click center point", "Lawn Mower", 0)

                        self.rect_from_center_fixed_tool = RectFromCenterFixedTool(self.canvas,
                                                                                   self.alongTLength.value(),
                                                                                   self.acrossTLength.value())
                        self.canvas.setMapTool(self.rect_from_center_fixed_tool)
                        self.rect_from_center_fixed_tool.msgbar.connect(self.pass_message_bar)
                        self.rect_from_center_fixed_tool.rb_reset_signal.connect(self.clear_initial_point)
                        self.rect_from_center_fixed_tool.rbFinished.connect(self.create_mission_area)
                    return

                else:
                    QMessageBox.warning(None,
                                        "Mission Template",
                                        "<center>Track lengths must be different from zero. </center>",
                                        QMessageBox.Close)

                    return
        else:

            self.deactivate_tool()

            self.rubber_band.reset(QgsWkbTypes.LineGeometry)
            self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
            self.initial_point.reset(QgsWkbTypes.PointGeometry)
            self.missionAreaDefined = False
            self.draw_mission_area()

    def clear_initial_point(self):
        """
        Reset the initial point rubber band from canvas
        """
        if self.initial_point is not None:
            self.initial_point.reset(QgsWkbTypes.PointGeometry)

    def change_initial_point(self):
        """
        Change the initial point
        """
        self.clear_initial_point()
        if self.area_points is not None:
            area = self.area_points
            index = self.initial_point_comboBox.currentIndex()

            self.initial_point.addPoint(area[index])

    def create_mission_area(self, geom):
        self.pass_message_bar("")
        self.clear_initial_point()
        if geom is not None:
            # Store points to variables
            self.area_points = [QgsPointXY(geom.vertexAt(0)),
                                QgsPointXY(geom.vertexAt(1)),
                                QgsPointXY(geom.vertexAt(2)),
                                QgsPointXY(geom.vertexAt(3))]

            self.change_initial_point()

            self.missionAreaDefined = True

    def preview_tracks(self):
        """ preview tracks on the canvas"""
        if self.missionAreaDefined:
            if self.altitudeButton.isChecked() and self.depthAltitudeBox.value() == 0:
                QMessageBox.warning(None,
                                    "Mission Template",
                                    "<center>Altitude must be different from zero. </center>",
                                    QMessageBox.Close)
            else:
                self.wp_list = self.compute_tracks(self.get_area_points())
                self.track_to_mission(self.wp_list, self.get_z(), self.get_altitude_mode(),
                                                self.get_speed(),
                                                self.get_x_tolerance(), self.get_y_tolerance(), self.get_z_tolerance())

                # show rubber band with temporal tracks
                self.rubber_band.reset(QgsWkbTypes.LineGeometry)
                self.rubber_band_points.reset(QgsWkbTypes.PointGeometry)
                self.initial_point.reset(QgsWkbTypes.PointGeometry)
                for wp in self.wp_list:
                    self.rubber_band.addPoint(QgsPointXY(wp.x(), wp.y()))
                    self.rubber_band_points.addPoint(QgsPointXY(wp.x(), wp.y()))
                self.rubber_band_points.show()
                self.rubber_band.show()

                self.unset_map_tool()
        else:
            QMessageBox.warning(None,
                                "Mission Template",
                                "<center>Define first an area for the mission. </center>",
                                QMessageBox.Close)

    def get_area_points(self):
        return self.area_points

    def get_num_across_tracks(self):
        return int(self.numAcrossTracks.value())

    def get_altitude_mode(self):
        return self.altitudeButton.isChecked()

    def get_z(self):
        return self.depthAltitudeBox.value()

    def get_speed(self):
        return self.speed_doubleSpinBox.value()

    def get_x_tolerance(self):
        return self.x_tolerance_doubleSpinBox.value()

    def get_y_tolerance(self):
        return self.y_tolerance_doubleSpinBox.value()

    def get_z_tolerance(self):
        return self.z_tolerance_doubleSpinBox.value()

    def get_mission_type(self):
        return self.template_type

    def compute_tracks(self, area_points):

        """
             Compute rectangle tracks
            :param area_points: points defining the extent of the tracks, they should be in WGS 84 lat/lon.
                                first two points define the along track direction.
            :return: list of ordered waypoints of rectangle trajectory
            """

        index = self.initial_point_comboBox.currentIndex()
        if index == 0:
            new_area = [area_points[0],
                        area_points[1],
                        area_points[2],
                        area_points[3]]
        elif index == 1:
            new_area = [area_points[1],
                        area_points[2],
                        area_points[3],
                        area_points[0]]
        elif index == 2:
            new_area = [area_points[2],
                        area_points[3],
                        area_points[0],
                        area_points[1]]
        elif index == 3:
            new_area = [area_points[3],
                        area_points[0],
                        area_points[1],
                        area_points[2]]

        new_area.append(area_points[index])

        return new_area

    def track_to_mission(self, wp_list, z, altitude_mode, speed, tolerance_x, tolerance_y, tolerance_z):

        self.mission = Mission()
        for wp in range(len(wp_list)):
            if wp == 0:
                # first step type waypoint
                step = MissionStep()
                mwp = MissionWaypoint(MissionPosition(wp_list[wp].y(), wp_list[wp].x(), z, altitude_mode),
                                      speed,
                                      MissionTolerance(tolerance_x, tolerance_y, tolerance_z))
                step.add_maneuver(mwp)
                self.mission.add_step(step)
            else:
                # rest of steps type section
                step = MissionStep()
                mwp = MissionSection(MissionPosition(wp_list[wp - 1].y(), wp_list[wp - 1].x(), z, altitude_mode),
                                     MissionPosition(wp_list[wp].y(), wp_list[wp].x(), z, altitude_mode),
                                     speed,
                                     MissionTolerance(tolerance_x, tolerance_y, tolerance_z))
                step.add_maneuver(mwp)
                self.mission.add_step(step)

    def delete_widget(self):
        self.delete_all(self.layout())
        self.deleteLater()
        self.close()

    def delete_all(self, layout):
        """
        delete all widget from layout
        :param layout: layout is a qt layout
        """
        if layout is not None:
            for i in reversed(range(layout.count())):
                item = layout.takeAt(i)
                widget = item.widget()
                if widget is not None:
                    widget.deleteLater()
                else:
                    self.delete_all(item.layout())

    def unset_map_tool(self):
        """
        Unset map tool from canvas.
        """
        if self.automaticExtent.isChecked():
            if self.onTarget:
                self.canvas.unsetMapTool(self.rect_from_center_tool)
            else:
                self.canvas.unsetMapTool(self.rectBy3Points_tool)
        if self.fixedExtent.isChecked():
            if self.onTarget:
                self.canvas.unsetMapTool(self.rect_from_center_fixed_tool)
            else:
                self.canvas.unsetMapTool(self.rectByFixedExtentTool)

    def deactivate_tool(self):
        """
        Deactivate tool.
        """
        if self.rectBy3Points_tool:
            self.rectBy3Points_tool.deactivate()
        elif self.rectByFixedExtentTool:
            self.rectByFixedExtentTool.deactivate()
        if self.rect_from_center_tool:
            self.rect_from_center_tool.deactivate()
        elif self.rect_from_center_fixed_tool:
            self.rect_from_center_fixed_tool.deactivate()

    def close(self):

        self.unset_map_tool()
        self.deactivate_tool()

        if self.rubber_band is not None:
            self.canvas.scene().removeItem(self.rubber_band)
            del self.rubber_band
            self.rubber_band = None
        if self.rubber_band_points is not None:
            self.canvas.scene().removeItem(self.rubber_band_points)
            del self.rubber_band_points
            self.rubber_band_points = None
        if self.initial_point is not None:
            self.canvas.scene().removeItem(self.initial_point)
            del self.initial_point
            self.initial_point = None
class quickFinder(QObject):

    name = u"&Quick Finder"
    actions = None
    toolbar = None
    finders = {}

    loadingIcon = None

    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.actions = {}
        self.finders = {}
        self.settings = MySettings()

        self._initFinders()

        self.iface.projectRead.connect(self._reloadFinders)
        self.iface.newProjectCreated.connect(self._reloadFinders)

        # translation environment
        self.plugin_dir = os.path.dirname(__file__)
        locale = QSettings().value("locale/userLocale")[0:2]
        localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale))
        if os.path.exists(localePath):
            self.translator = QTranslator()
            self.translator.load(localePath)
            QCoreApplication.installTranslator(self.translator)

    def initGui(self):
        self.actions['showSettings'] = QAction(
            QIcon(":/plugins/quickfinder/icons/settings.svg"),
            self.tr(u"&Settings"),
            self.iface.mainWindow())
        self.actions['showSettings'].triggered.connect(self.showSettings)
        self.iface.addPluginToMenu(self.name, self.actions['showSettings'])
        self.actions['help'] = QAction(
            QIcon(":/plugins/quickfinder/icons/help.svg"),
            self.tr("Help"),
            self.iface.mainWindow())
        self.actions['help'].triggered.connect(
            lambda: QDesktopServices().openUrl(
                QUrl("http://3nids.github.io/quickfinder")))
        self.iface.addPluginToMenu(self.name, self.actions['help'])
        self._initToolbar()
        self.rubber = QgsRubberBand(self.iface.mapCanvas())
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

    def unload(self):
        """ Unload plugin """
        for key in self.finders.keys():
            self.finders[key].close()
        for action in self.actions.itervalues():
            self.iface.removePluginMenu(self.name, action)
        if self.toolbar:
            del self.toolbar
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    def _initToolbar(self):
        """setup the plugin toolbar."""
        self.toolbar = self.iface.addToolBar(self.name)
        self.toolbar.setObjectName('mQuickFinderToolBar')
        self.searchAction = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"),
                                     self.tr("Search"),
                                     self.toolbar)
        self.stopAction = QAction(
            QIcon(":/plugins/quickfinder/icons/wrong2.svg"),
            self.tr("Cancel"),
            self.toolbar)
        self.finderBox = FinderBox(self.finders, self.iface, self.toolbar)
        self.finderBox.searchStarted.connect(self.searchStarted)
        self.finderBox.searchFinished.connect(self.searchFinished)
        self.finderBoxAction = self.toolbar.addWidget(self.finderBox)
        self.finderBoxAction.setVisible(True)
        self.searchAction.triggered.connect(self.finderBox.search)
        self.toolbar.addAction(self.searchAction)
        self.stopAction.setVisible(False)
        self.stopAction.triggered.connect(self.finderBox.stop)
        self.toolbar.addAction(self.stopAction)
        self.toolbar.setVisible(True)

    def _initFinders(self):
        self.finders['geomapfish'] = GeomapfishFinder(self)
        self.finders['osm'] = OsmFinder(self)
        self.finders['project'] = ProjectFinder(self)
        for key in self.finders.keys():
            self.finders[key].message.connect(self.displayMessage)
        self.refreshProject()

    def _reloadFinders(self):
        for key in self.finders.keys():
            self.finders[key].close()
            self.finders[key].reload()
        self.refreshProject()

    @pyqtSlot(str, QgsMessageBar.MessageLevel)
    def displayMessage(self, message, level):
        self.iface.messageBar().pushMessage("QuickFinder", message, level)

    def showSettings(self):
        if ConfigurationDialog().exec_():
            self._reloadFinders()

    def searchStarted(self):
        self.searchAction.setVisible(False)
        self.stopAction.setVisible(True)

    def searchFinished(self):
        self.searchAction.setVisible(True)
        self.stopAction.setVisible(False)

    def refreshProject(self):
        if not self.finders['project'].activated:
            return
        if not self.settings.value("refreshAuto"):
            return
        nDays = self.settings.value("refreshDelay")
        # do not ask more ofen than 3 days
        askLimit = min(3, nDays)
        recentlyAsked = self.settings.value("refreshLastAsked") >= nDaysAgoIsoDate(askLimit)
        if recentlyAsked:
            return
        threshDate = nDaysAgoIsoDate(nDays)
        uptodate = True
        for search in self.finders['project'].searches.values():
            if search.dateEvaluated <= threshDate:
                uptodate = False
                break
        if uptodate:
            return
        self.settings.setValue("refreshLastAsked", nDaysAgoIsoDate(0))
        ret = QMessageBox(QMessageBox.Warning,
                          "Quick Finder",
                          QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"),
                          QMessageBox.Cancel | QMessageBox.Yes).exec_()
        if ret == QMessageBox.Yes:
            RefreshDialog(self.finders['project']).exec_()
class GeomapfishLocatorFilter(QgsLocatorFilter):

    USER_AGENT = b'Mozilla/5.0 QGIS GeoMapFish Locator Filter'

    def __init__(self, iface: QgisInterface = None):
        super().__init__()
        self.rubber_band = None
        self.settings = Settings()
        self.iface = None
        self.map_canvas = None
        self.current_timer = None
        self.transform = None

        # only get map_canvas on main thread, not when cloning
        if iface is not None:
            self.iface = iface
            self.map_canvas = iface.mapCanvas()
            self.map_canvas.destinationCrsChanged.connect(
                self.create_transform)

            self.rubber_band = QgsRubberBand(self.map_canvas)
            self.rubber_band.setColor(QColor(255, 255, 50, 200))
            self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE)
            self.rubber_band.setIconSize(15)
            self.rubber_band.setWidth(4)
            self.rubber_band.setBrushStyle(Qt.NoBrush)

            self.create_transform()

    def name(self) -> str:
        return self.__class__.__name__

    def clone(self):
        return GeomapfishLocatorFilter()

    def displayName(self) -> str:
        name = self.settings.value("filter_name")
        if name != '':
            return name
        return self.tr('Geomapfish service')

    def prefix(self) -> str:
        return 'gmf'

    def hasConfigWidget(self) -> bool:
        return True

    def openConfigWidget(self, parent=None):
        ConfigDialog(parent).exec_()

    def create_transform(self):
        srv_crs_authid = self.settings.value('geomapfish_crs')
        src_crs = QgsCoordinateReferenceSystem(srv_crs_authid)
        assert src_crs.isValid()
        dst_crs = self.map_canvas.mapSettings().destinationCrs()
        self.transform = QgsCoordinateTransform(src_crs, dst_crs,
                                                QgsProject.instance())

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url.url()

    def emit_bad_configuration(self, err=None):
        result = QgsLocatorResult()
        result.filter = self
        result.displayString = self.tr('Locator filter is not configured.')
        result.description = err if err else self.tr(
            'Double-click to configure.')
        result.userData = FilterNotConfigured
        result.icon = QgsApplication.getThemeIcon('mIconWarning.svg')
        self.resultFetched.emit(result)
        return

    @pyqtSlot()
    def clear_results(self):
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.PointGeometry)
        if self.current_timer is not None:
            self.current_timer.timeout.disconnect(self.clear_results)
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def fetchResults(self, search, context, feedback):
        try:
            self.dbg_info("start GMF locator search...")

            url = self.settings.value('geomapfish_url')

            if url == "":
                self.emit_bad_configuration()
                return

            params = {
                'query': search,
                'limit': str(self.settings.value('total_limit')),
                'partitionlimit': str(self.settings.value('category_limit'))
            }

            if len(search) < 2:
                return

            headers = {b'User-Agent': self.USER_AGENT}
            if self.settings.value('geomapfish_user') != '':
                user = self.settings.value('geomapfish_user')
                password = self.settings.value('geomapfish_pass')
                auth_data = "{}:{}".format(user, password)
                b64 = QByteArray(auth_data.encode()).toBase64()
                headers[QByteArray('Authorization'.encode())] = QByteArray(
                    'Basic '.encode()) + b64

            url = self.url_with_param(url, params)
            self.dbg_info(url)

            nam = NetworkAccessManager()
            feedback.canceled.connect(nam.abort)
            (response, content) = nam.request(url,
                                              headers=headers,
                                              blocking=True)
            self.handle_response(response, content)

        except RequestsExceptionUserAbort:
            pass
        except RequestsException as err:
            self.emit_bad_configuration(str(err))
            self.info(err)
        except Exception as e:
            self.info(str(e), Qgis.Critical)
            #exc_type, exc_obj, exc_traceback = sys.exc_info()
            #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1]
            #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical)
            #self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical)

        finally:
            self.finished.emit()

    def handle_response(self, response, content):
        try:
            if response.status_code != 200:
                self.info("Error with status code: {}".format(
                    response.status_code))
                return

            data = json.loads(content.decode('utf-8'))
            #self.dbg_info(data)

            features = data['features']
            for f in features:
                json_geom = json.dumps(f['geometry'])
                ogr_geom = ogr.CreateGeometryFromJson(json_geom)
                wkt = ogr_geom.ExportToWkt()
                geometry = QgsGeometry.fromWkt(wkt)
                self.dbg_info('---------')
                self.dbg_info(
                    QgsWkbTypes.geometryDisplayString(geometry.type()))
                self.dbg_info(f.keys())
                self.dbg_info('{} {}'.format(f['properties']['layer_name'],
                                             f['properties']['label']))
                self.dbg_info(f['bbox'])
                self.dbg_info(f['geometry'])
                if geometry is None:
                    continue
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = f['properties']['label']
                if Qgis.QGIS_VERSION_INT >= 30100:
                    result.group = self.beautify_group(
                        f['properties']['layer_name'])
                result.userData = geometry
                self.resultFetched.emit(result)

        except Exception as e:
            self.info(str(e), Qgis.Critical)
            #exc_type, exc_obj, exc_traceback = sys.exc_info()
            #filename = os.path.split(exc_traceback.tb_frame.f_code.co_filename)[1]
            #self.info('{} {} {}'.format(exc_type, filename, exc_traceback.tb_lineno), Qgis.Critical)
            # self.info(traceback.print_exception(exc_type, exc_obj, exc_traceback), Qgis.Critical)

    def triggerResult(self, result):
        self.clear_results()
        if result.userData == FilterNotConfigured:
            self.openConfigWidget()
            if self.iface and hasattr(self.iface, 'invalidateLocatorResults'):
                # from QGIS 3.2 iface has invalidateLocatorResults
                self.iface.invalidateLocatorResults()
            return

        # this should be run in the main thread, i.e. mapCanvas should not be None
        geometry = result.userData
        geometry.transform(self.transform)

        self.rubber_band.reset(geometry.type())
        self.rubber_band.addGeometry(geometry, None)
        rect = geometry.boundingBox()
        rect.scale(1.5)
        self.map_canvas.setExtent(rect)
        self.map_canvas.refresh()

        self.current_timer = QTimer()
        self.current_timer.timeout.connect(self.clear_results)
        self.current_timer.setSingleShot(True)
        self.current_timer.start(5000)

    def beautify_group(self, group) -> str:
        if self.settings.value("remove_leading_digits"):
            group = re.sub('^[0-9]+', '', group)
        if self.settings.value("replace_underscore"):
            group = group.replace("_", " ")
        if self.settings.value("break_camelcase"):
            group = self.break_camelcase(group)
        return group

    def info(self, msg="", level=Qgis.Info):
        QgsMessageLog.logMessage('{} {}'.format(self.__class__.__name__, msg),
                                 'QgsLocatorFilter', level)

    def dbg_info(self, msg=""):
        if DEBUG:
            self.info(msg)

    @staticmethod
    def break_camelcase(identifier) -> str:
        matches = re.finditer(
            '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)',
            identifier)
        return ' '.join([m.group(0) for m in matches])
class Geo360Dialog(QDockWidget, Ui_orbitalDialog):
    """Geo360 Dialog Class"""
    def __init__(self, iface, parent=None, featuresId=None, layer=None):

        QDockWidget.__init__(self)

        self.setupUi(self)

        self.DEFAULT_URL = ("http://" + config.IP + ":" + str(config.PORT) +
                            "/viewer.html")
        self.DEFAULT_EMPTY = ("http://" + config.IP + ":" + str(config.PORT) +
                              "/none.html")
        self.DEFAULT_BLANK = ("http://" + config.IP + ":" + str(config.PORT) +
                              "/blank.html")

        # Create Viewer
        self.CreateViewer()

        self.plugin_path = os.path.dirname(os.path.realpath(__file__))
        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.parent = parent

        # Orientation from image
        self.yaw = math.pi
        self.bearing = None

        self.layer = layer
        self.featuresId = featuresId

        self.actualPointDx = None
        self.actualPointSx = None
        self.actualPointOrientation = None

        self.selected_features = qgsutils.getToFeature(self.layer,
                                                       self.featuresId)

        # Get image path
        self.current_image = self.GetImage()

        # Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"There is no associated image.")
            self.resetQgsRubberBand()
            self.ChangeUrlViewer(self.DEFAULT_EMPTY)
            return

        # Copy file to local server
        self.CopyFile(self.current_image)

        # Set RubberBand
        self.resetQgsRubberBand()
        self.setOrientation()
        self.setPosition()

    """Update data from Marzipano Viewer"""

    def onNewData(self, data):
        try:
            newYaw = float(data[0])
            self.UpdateOrientation(yaw=newYaw)
        except:
            None

    def CreateViewer(self):
        """Create Viewer"""
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Create viewer",
                                       onlyLog=True)

        self.cef_widget = QWebView()
        self.cef_widget.setContextMenuPolicy(Qt.NoContextMenu)

        self.cef_widget.settings().setAttribute(QWebSettings.JavascriptEnabled,
                                                True)
        pano_view_settings = self.cef_widget.settings()
        pano_view_settings.setAttribute(QWebSettings.WebGLEnabled, True)
        # pano_view_settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
        pano_view_settings.setAttribute(
            QWebSettings.Accelerated2dCanvasEnabled, True)
        pano_view_settings.setAttribute(QWebSettings.JavascriptEnabled, True)

        self.page = _ViewerPage()
        self.page.newData.connect(self.onNewData)
        self.cef_widget.setPage(self.page)

        self.cef_widget.load(QUrl(self.DEFAULT_URL))
        self.ViewerLayout.addWidget(self.cef_widget, 1, 0)

    # def SetInitialYaw(self):
    #     """Set Initial Viewer Yaw"""
    #     self.bearing = self.selected_features.attribute(config.column_yaw)
    #     # self.view.browser.GetMainFrame().ExecuteFunction("InitialYaw",
    #     #                                                  self.bearing)
    #     return

    def RemoveImage(self):
        """Remove Image"""
        try:
            os.remove(self.plugin_path + "/viewer/image.jpg")
        except OSError:
            pass

    def CopyFile(self, src):
        """Copy Image File in Local Server"""
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Copying image",
                                       onlyLog=True)

        src_dir = src
        dst_dir = self.plugin_path + "/viewer"

        # Copy image in local folder
        img = Image.open(src_dir)
        rgb_im = img.convert("RGB")
        dst_dir = dst_dir + "/image.jpg"

        try:
            os.remove(dst_dir)
        except OSError:
            pass

        rgb_im.save(dst_dir)

    def GetImage(self):
        """Get Selected Image"""
        try:
            path = qgsutils.getAttributeFromFeature(self.selected_features,
                                                    config.column_name)
            if not os.path.isabs(path):  # Relative Path to Project
                path_project = QgsProject.instance().readPath("./")
                path = os.path.normpath(os.path.join(path_project, path))
        except Exception:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"Column not found.")
            return

        qgsutils.showUserAndLogMessage(u"Information: ",
                                       str(path),
                                       onlyLog=True)
        return path

    def ChangeUrlViewer(self, new_url):
        """Change Url Viewer"""
        self.cef_widget.load(QUrl(new_url))

    def ReloadView(self, newId):
        """Reaload Image viewer"""
        self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                            | Qt.WindowActive)
        # this will activate the window
        self.activateWindow()
        self.selected_features = qgsutils.getToFeature(self.layer, newId)

        self.current_image = self.GetImage()

        # Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"There is no associated image.")
            self.ChangeUrlViewer(self.DEFAULT_EMPTY)
            self.resetQgsRubberBand()
            return

        # Set RubberBand
        self.resetQgsRubberBand()
        self.setOrientation()
        self.setPosition()

        # Copy file to local server
        self.CopyFile(self.current_image)

        self.ChangeUrlViewer(self.DEFAULT_URL)

    def GetBackNextImage(self):
        """Get to Back Image"""
        sender = QObject.sender(self)

        lys = self.canvas.layers()  # Check if mapa foto is loaded
        if len(lys) == 0:
            qgsutils.showUserAndLogMessage(u"Information: ",
                                           u"You need load the photo layer.")
            return

        for layer in lys:
            if layer.name() == config.layer_name:
                self.encontrado = True
                self.iface.setActiveLayer(layer)

                f = self.selected_features

                ac_lordem = f.attribute(config.column_order)

                if sender.objectName() == "btn_back":
                    new_lordem = int(ac_lordem) - 1
                else:
                    new_lordem = int(ac_lordem) + 1

                # Filter mapa foto layer
                ids = [
                    feat.id() for feat in layer.getFeatures(QgsFeatureRequest(
                    ).setFilterExpression(config.column_order + " ='" +
                                          str(new_lordem) + "'"))
                ]

                if len(ids) == 0:
                    qgsutils.showUserAndLogMessage(
                        u"Information: ",
                        u"There is no superiority that follows.")
                    # Filter mapa foto layer
                    ids = [
                        feat.id()
                        for feat in layer.getFeatures(QgsFeatureRequest(
                        ).setFilterExpression(config.column_order + " ='" +
                                              str(ac_lordem) + "'"))
                    ]
                # Update selected feature
                self.ReloadView(ids[0])

        if self.encontrado is False:
            qgsutils.showUserAndLogMessage(
                u"Information: ",
                u"You need a layer with images and set the name in the config.py file.",
            )

        return

    def FullScreen(self, value):
        """FullScreen action button"""
        qgsutils.showUserAndLogMessage(u"Information: ",
                                       u"Fullscreen.",
                                       onlyLog=True)
        if value:
            self.showFullScreen()
        else:
            self.showNormal()

    def UpdateOrientation(self, yaw=None):
        """Update Orientation"""
        self.bearing = self.selected_features.attribute(config.column_yaw)
        try:
            self.actualPointOrientation.reset()
        except Exception:
            pass

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QgsWkbTypes.LineGeometry)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)
        self.actualPointOrientation.addPoint(self.actualPointDx)

        # End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPointXY(
            float(A1x), float(A1y)))

        # Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()
        rotatePoint = self.rotateTool.rotate(tmpGeom, self.actualPointDx,
                                             angle)

        self.actualPointOrientation.setToGeometry(rotatePoint, self.dumLayer)
        # Set Azimut value
        tmpGeom = rotatePoint.asPolyline()
        azim = tmpGeom[0].azimuth(tmpGeom[1])
        if azim < 0:
            azim += 360
        self.yawLbl.setText("Yaw : " + str(round(yaw, 2)) + " Azimut : " +
                            str(round(azim, 2)))

    def setOrientation(self, yaw=None):
        """Set Orientation in the firt time"""
        self.bearing = self.selected_features.attribute(config.column_yaw)

        originalPoint = self.selected_features.geometry().asPoint()
        self.actualPointDx = qgsutils.convertProjection(
            originalPoint.x(),
            originalPoint.y(),
            self.layer.crs().authid(),
            self.canvas.mapSettings().destinationCrs().authid(),
        )

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QgsWkbTypes.LineGeometry)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)

        self.actualPointOrientation.addPoint(self.actualPointDx)

        # End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPointXY(
            float(A1x), float(A1y)))
        # Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()

        self.rotateTool = transformGeometry()
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        self.dumLayer = QgsVectorLayer("Point?crs=" + epsg, "temporary_points",
                                       "memory")
        self.actualPointOrientation.setToGeometry(
            self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle),
            self.dumLayer)

    def setPosition(self):
        """Set RubberBand Position"""
        # Transform Point
        originalPoint = self.selected_features.geometry().asPoint()
        self.actualPointDx = qgsutils.convertProjection(
            originalPoint.x(),
            originalPoint.y(),
            "EPSG:4326",
            self.canvas.mapSettings().destinationCrs().authid(),
        )

        self.positionDx = QgsRubberBand(self.iface.mapCanvas(),
                                        QgsWkbTypes.PointGeometry)
        self.positionDx.setWidth(6)
        self.positionDx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionDx.setIconSize(6)
        self.positionDx.setColor(Qt.black)
        self.positionSx = QgsRubberBand(self.iface.mapCanvas(),
                                        QgsWkbTypes.PointGeometry)
        self.positionSx.setWidth(5)
        self.positionSx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionSx.setIconSize(4)
        self.positionSx.setColor(Qt.blue)
        self.positionInt = QgsRubberBand(self.iface.mapCanvas(),
                                         QgsWkbTypes.PointGeometry)
        self.positionInt.setWidth(5)
        self.positionInt.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionInt.setIconSize(3)
        self.positionInt.setColor(Qt.white)

        self.positionDx.addPoint(self.actualPointDx)
        self.positionSx.addPoint(self.actualPointDx)
        self.positionInt.addPoint(self.actualPointDx)

    def closeEvent(self, _):
        """Close dialog"""
        self.resetQgsRubberBand()
        self.canvas.refresh()
        self.iface.actionPan().trigger()
        self.parent.orbitalViewer = None
        self.RemoveImage()

    def resetQgsRubberBand(self):
        """Remove RubbeBand"""
        try:
            self.yawLbl.setText("")
            self.positionSx.reset()
            self.positionInt.reset()
            self.positionDx.reset()
            self.actualPointOrientation.reset()
        except Exception:
            None
Example #36
0
class ShLocatorFilter(QgsLocatorFilter):

    HEADERS = {b'User-Agent': b'Mozilla/5.0 QGIS ShLocator Filter'}

    message_emitted = pyqtSignal(str, str, Qgis.MessageLevel, QWidget)

    def __init__(self, iface: QgisInterface = None):
        """"
        :param iface: QGIS interface, given when on the main thread (which will display/trigger results), None otherwise
        """
        super().__init__()

        self.iface = iface
        self.settings = Settings()

        #  following properties will only be used in main thread
        self.rubber_band = None
        self.map_canvas: QgsMapCanvas = None
        self.transform_ch = None
        self.current_timer = None
        self.result_found = False
        self.nam_fetch_feature = None

        if iface is not None:
            # happens only in main thread
            self.map_canvas = iface.mapCanvas()
            self.map_canvas.destinationCrsChanged.connect(
                self.create_transforms)

            self.rubber_band = QgsRubberBand(
                self.map_canvas, QgsWkbTypes.PolygonGeometry)
            self.rubber_band.setColor(QColor(255, 50, 50, 200))
            self.rubber_band.setFillColor(QColor(255, 255, 50, 160))
            self.rubber_band.setBrushStyle(Qt.SolidPattern)
            self.rubber_band.setLineStyle(Qt.SolidLine)
            self.rubber_band.setIcon(self.rubber_band.ICON_CIRCLE)
            self.rubber_band.setIconSize(15)
            self.rubber_band.setWidth(4)
            self.rubber_band.setBrushStyle(Qt.NoBrush)

            self.create_transforms()

    def name(self):
        return 'ShLocator'

    def clone(self):
        return ShLocatorFilter()

    def priority(self):
        return QgsLocatorFilter.Highest

    def displayName(self):
        return 'ShLocator'

    def prefix(self):
        return 'shl'

    def clearPreviousResults(self):
        if self.rubber_band:
            self.rubber_band.reset(QgsWkbTypes.PointGeometry)

        if self.current_timer is not None:
            self.current_timer.stop()
            self.current_timer.deleteLater()
            self.current_timer = None

    def hasConfigWidget(self):
        return True

    def openConfigWidget(self, parent=None):
        dlg = ConfigDialog(parent)
        dlg.exec_()

    def create_transforms(self):
        # this should happen in the main thread
        src_crs_ch = QgsCoordinateReferenceSystem('EPSG:2056')
        assert src_crs_ch.isValid()
        dst_crs = self.map_canvas.mapSettings().destinationCrs()
        self.transform_ch = QgsCoordinateTransform(
            src_crs_ch, dst_crs, QgsProject.instance())

    @staticmethod
    def url_with_param(url, params) -> str:
        url = QUrl(url)
        q = QUrlQuery(url)
        for key, value in params.items():
            q.addQueryItem(key, value)
        url.setQuery(q)
        return url.url()

    def fetchResults(self, search: str, context: QgsLocatorContext, feedback: QgsFeedback):
        try:
            dbg_info("start shlocator search...")

            if len(search) < 3:
                return

            self.result_found = False

            params = {
                'query': str(search),
                'maxfeatures': str(self.settings.value('max_features'))
            }

            nam = NetworkAccessManager()
            feedback.canceled.connect(nam.abort)
            url = self.url_with_param(
                self.settings.value('service_url'), params)
            dbg_info(url)
            try:
                (response, content) = nam.request(
                    url, headers=self.HEADERS, blocking=True)
                self.handle_response(response, search)
            except RequestsExceptionUserAbort:
                pass
            except RequestsException as err:
                info(err, Qgis.Info)

            if not self.result_found:
                result = QgsLocatorResult()
                result.filter = self
                result.displayString = self.tr('No result found.')
                result.userData = NoResult
                self.resultFetched.emit(result)

        except Exception as e:
            info(e, Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            info('{} {} {}'.format(exc_type, filename,
                                   exc_traceback.tb_lineno), Qgis.Critical)
            info(traceback.print_exception(
                exc_type, exc_obj, exc_traceback), Qgis.Critical)

    def data_product_qgsresult(self, data: dict) -> QgsLocatorResult:
        result = QgsLocatorResult()
        result.filter = self
        result.displayString = data['display']
        result.group = 'Karten'
        result.userData = DataProductResult(
            type=data['type'],
            dataproduct_id=data['dataproduct_id'],
            display=data['display'],
            dset_info=data['dset_info'],
            sublayers=data.get('sublayers', None)
        )
        data_product = 'dataproduct'
        data_type = data['type']
        result.icon, result.description = dataproduct2icon_description(
            data_product, data_type)
        return result

    def handle_response(self, response, search_text: str):
        try:
            if response.status_code != 200:
                if not isinstance(response.exception, RequestsExceptionUserAbort):
                    info("Error in main response with status code: "
                         "{} from {}".format(response.status_code, response.url))
                return

            display_name_field = QgsField('display_name', QVariant.String)
            fields = QgsFields()
            fields.append(display_name_field)
            features = QgsJsonUtils.stringToFeatureList(response.content.decode('utf-8'), fields, QTextCodec.codecForName('UTF-8'))
            dbg_info('Found {} features'.format(len(features)))
            dbg_info('Data {}'.format(response.content.decode('utf-8')))

            for feature in features:
                dbg_info('Adding feature {}'.format(feature['display_name']))
                result = QgsLocatorResult()
                result.filter = self
                result.group = 'Objekte'
                result.displayString = feature['display_name']
                result.userData = FeatureResult(feature)
                self.resultFetched.emit(result)

                self.result_found = True

        except Exception as e:
            info(str(e), Qgis.Critical)
            exc_type, exc_obj, exc_traceback = sys.exc_info()
            filename = os.path.split(
                exc_traceback.tb_frame.f_code.co_filename)[1]
            info('{} {} {}'.format(exc_type, filename,
                                   exc_traceback.tb_lineno), Qgis.Critical)
            info(traceback.print_exception(
                exc_type, exc_obj, exc_traceback), Qgis.Critical)

    def triggerResult(self, result: QgsLocatorResult):
        # this is run in the main thread, i.e. map_canvas is not None
        self.clearPreviousResults()

        if type(result.userData) == NoResult:
            pass
        elif type(result.userData) == FeatureResult:
            self.highlight(result.userData.feature.geometry())
        else:
            info('Incorrect result. Please contact support', Qgis.Critical)

    def highlight(self, geometry: QgsGeometry):
        self.rubber_band.reset(geometry.type())
        self.rubber_band.addGeometry(geometry, None)

        rect = geometry.boundingBox()
        if not self.settings.value('keep_scale'):
            if rect.isEmpty():
                current_extent = self.map_canvas.extent()
                rect = current_extent.scaled(self.settings.value(
                    'point_scale')/self.map_canvas.scale(), rect.center())
            else:
                rect.scale(4)
        self.map_canvas.setExtent(rect)
        self.map_canvas.refresh()

#        self.current_timer = QTimer()
#        self.current_timer.timeout.connect(self.clearPreviousResults)
#        self.current_timer.setSingleShot(True)
#        self.current_timer.start(5000)

    def parse_feature_response(self, response):
        if response.status_code != 200:
            if not isinstance(response.exception, RequestsExceptionUserAbort):
                info("Error in feature response with status code: "
                     "{} from {}".format(response.status_code, response.url))
            return

        data = json.loads(response.content.decode('utf-8'))

        geometry_type = data['geometry']['type']
        geometry = QgsGeometry()

        if geometry_type == 'Point':
            geometry = QgsGeometry.fromPointXY(QgsPointXY(data['geometry']['coordinates'][0],
                                                          data['geometry']['coordinates'][1]))

        elif geometry_type == 'Polygon':
            rings = data['geometry']['coordinates']
            for r in range(0, len(rings)):
                for p in range(0, len(rings[r])):
                    rings[r][p] = QgsPointXY(rings[r][p][0], rings[r][p][1])
            geometry = QgsGeometry.fromPolygonXY(rings)

        elif geometry_type == 'MultiPolygon':
            islands = data['geometry']['coordinates']
            for i in range(0, len(islands)):
                for r in range(0, len(islands[i])):
                    for p in range(0, len(islands[i][r])):
                        islands[i][r][p] = QgsPointXY(
                            islands[i][r][p][0], islands[i][r][p][1])
            geometry = QgsGeometry.fromMultiPolygonXY(islands)

        else:
            # ShLocator does not handle {geometry_type} yet. Please contact support
            info('ShLocator unterstützt den Geometrietyp {geometry_type} nicht.'
                 ' Bitte kontaktieren Sie den Support.'.format(geometry_type=geometry_type), Qgis.Warning)

        geometry.transform(self.transform_ch)
        self.highlight(geometry)
Example #37
0
class QuickFinder(QObject):

    name = u"&Quick Finder"
    actions = None
    toolbar = None
    finders = {}

    loadingIcon = None

    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.actions = {}
        self.finders = {}
        self.settings = MySettings()
        self.rubber = None

        self._init_finders()

        self.iface.projectRead.connect(self._reload_finders)
        self.iface.newProjectCreated.connect(self._reload_finders)

        # translation environment
        self.plugin_dir = os.path.dirname(__file__)
        locale = QSettings().value("locale/userLocale")[0:2]
        localePath = os.path.join(self.plugin_dir, 'i18n', 'quickfinder_{0}.qm'.format(locale))
        if os.path.exists(localePath):
            self.translator = QTranslator()
            self.translator.load(localePath)
            QCoreApplication.installTranslator(self.translator)

    def initGui(self):
        self.actions['showSettings'] = QAction(
            QIcon(":/plugins/quickfinder/icons/settings.svg"),
            self.tr(u"&Settings"),
            self.iface.mainWindow())
        self.actions['showSettings'].triggered.connect(self.show_settings)
        self.iface.addPluginToMenu(self.name, self.actions['showSettings'])
        self.actions['help'] = QAction(
            QIcon(":/plugins/quickfinder/icons/help.svg"),
            self.tr("Help"),
            self.iface.mainWindow())
        self.actions['help'].triggered.connect(
            lambda: QDesktopServices().openUrl(
                QUrl("http://3nids.github.io/quickfinder")))
        self.iface.addPluginToMenu(self.name, self.actions['help'])
        self._init_toolbar()
        self.rubber = QgsRubberBand(self.iface.mapCanvas())
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

    def unload(self):
        """ Unload plugin """
        for key in self.finders.keys():
            self.finders[key].close()
        for action in self.actions.itervalues():
            self.iface.removePluginMenu(self.name, action)
        if self.toolbar:
            del self.toolbar
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    def _init_toolbar(self):
        """setup the plugin toolbar."""
        self.toolbar = self.iface.addToolBar(self.name)
        self.toolbar.setObjectName('mQuickFinderToolBar')
        self.search_action = QAction(QIcon(":/plugins/quickfinder/icons/magnifier13.svg"), self.tr("Search"), self.toolbar)
        self.stop_action = QAction(QIcon(":/plugins/quickfinder/icons/wrong2.svg"), self.tr("Cancel"), self.toolbar)
        self.finder_box = FinderBox(self.finders, self.iface, self.toolbar)
        self.finder_box.search_started.connect(self.search_started)
        self.finder_box.search_finished.connect(self.search_finished)
        self.finder_box_action = self.toolbar.addWidget(self.finder_box)
        self.finder_box_action.setVisible(True)
        self.search_action.triggered.connect(self.finder_box.search)
        self.toolbar.addAction(self.search_action)
        self.stop_action.setVisible(False)
        self.stop_action.triggered.connect(self.finder_box.stop)
        self.toolbar.addAction(self.stop_action)
        self.toolbar.setVisible(True)

    def _init_finders(self):
        self.finders['geomapfish'] = GeomapfishFinder(self)
        self.finders['osm'] = OsmFinder(self)
        self.finders['project'] = ProjectFinder(self)
        for key in self.finders.keys():
            self.finders[key].message.connect(self.display_message)
        self.refresh_project()

    def _reload_finders(self):
        for key in self.finders.keys():
            self.finders[key].close()
            self.finders[key].reload()
        self.refresh_project()

    @pyqtSlot(str, QgsMessageBar.MessageLevel)
    def display_message(self, message, level):
        self.iface.messageBar().pushMessage("QuickFinder", message, level)

    def show_settings(self):
        if ConfigurationDialog().exec_():
            self._reload_finders()

    def search_started(self):
        self.search_action.setVisible(False)
        self.stop_action.setVisible(True)

    def search_finished(self):
        self.search_action.setVisible(True)
        self.stop_action.setVisible(False)

    def refresh_project(self):
        if not self.finders['project'].activated:
            return
        if not self.settings.value("refreshAuto"):
            return
        n_days = self.settings.value("refreshDelay")
        # do not ask more ofen than 3 days
        ask_limit = min(3, n_days)
        recently_asked = self.settings.value("refreshLastAsked") >= n_days_ago_iso_date(ask_limit)
        if recently_asked:
            return
        thresh_date = n_days_ago_iso_date(n_days)
        uptodate = True
        for search in self.finders['project'].searches.values():
            if search.dateEvaluated <= thresh_date:
                uptodate = False
                break
        if uptodate:
            return
        self.settings.setValue("refreshLastAsked", n_days_ago_iso_date(0))
        ret = QMessageBox(QMessageBox.Warning,
                          "Quick Finder",
                          QCoreApplication.translate("Auto Refresh", "Some searches are outdated. Do you want to refresh them ?"),
                          QMessageBox.Cancel | QMessageBox.Yes).exec_()
        if ret == QMessageBox.Yes:
            RefreshDialog(self.finders['project']).exec_()
class IntersectionDialog(QDialog, Ui_Intersection, SettingDialog):
    def __init__(self, iface, observations, initPoint):
        QDialog.__init__(self)
        self.setupUi(self)
        self.settings = MySettings()
        SettingDialog.__init__(self, self.settings, False, False)
        self.processButton.clicked.connect(self.doIntersection)
        self.okButton.clicked.connect(self.accept)
        self.finished.connect(self.resetRubber)
        self.initPoint = initPoint

        self.observations = []
        self.solution = None
        self.report = ""

        self.rubber = QgsRubberBand(iface.mapCanvas(), QGis.Point)
        self.rubber.setColor(self.settings.value("rubberColor"))
        self.rubber.setIcon(self.settings.value("rubberIcon"))
        self.rubber.setIconSize(self.settings.value("rubberSize"))

        self.observationTableWidget.displayRows(observations)
        self.observationTableWidget.itemChanged.connect(self.disbaleOKbutton)
        self.doIntersection()

    def resetRubber(self, dummy=0):
        self.rubber.reset()

    def disbaleOKbutton(self):
        self.okButton.setDisabled(True)

    def doIntersection(self):
        self.observations = []
        self.solution = None
        self.report = ""
        self.rubber.reset()

        observations = self.observationTableWidget.getObservations()
        nObs = len(observations)
        if nObs < 2:
            self.reportBrowser.setText(QCoreApplication.translate("IntersectIt",
                                                                  "No intersection can be done "
                                                                  "with less than 2 observations."))
            return
        if nObs == 2:
            if observations[0]["type"] == "distance" and observations[1]["type"] == "distance":
                intersection = TwoCirclesIntersection(observations, self.initPoint)
            elif observations[0]["type"] == "orientation" and observations[1]["type"] == "orientation":
                intersection = TwoOrientationIntersection(observations)
            else:
                intersection = DistanceOrientationIntersection(observations, self.initPoint)
        else:
            maxIter = self.advancedIntersecLSmaxIteration.value()
            threshold = self.advancedIntersecLSconvergeThreshold.value()
            intersection = LeastSquares(observations, self.initPoint, maxIter, threshold)

        self.reportBrowser.setText(intersection.report)

        if intersection.solution is not None:
            self.solution = intersection.solution
            self.observations = observations
            self.report = intersection.report
            self.okButton.setEnabled(True)
            self.rubber.setToGeometry(QgsGeometry().fromPoint(self.solution), None)
class DistanceMapTool(QgsMapTool):
    def __init__(self, iface):
        self.iface = iface
        self.mapCanvas = iface.mapCanvas()
        self.settings = MySettings()
        QgsMapTool.__init__(self, self.mapCanvas)

    def activate(self):
        QgsMapTool.activate(self)
        self.rubber = QgsRubberBand(self.mapCanvas, QGis.Point)
        self.rubber.setColor(self.settings.value("rubberColor"))
        self.rubber.setIcon(self.settings.value("rubberIcon"))
        self.rubber.setIconSize(self.settings.value("rubberSize"))
        self.updateSnapperList()
        self.mapCanvas.layersChanged.connect(self.updateSnapperList)
        self.mapCanvas.scaleChanged.connect(self.updateSnapperList)
        self.messageWidget = self.iface.messageBar().createMessage("Intersect It", "Not snapped.")
        self.messageWidgetExist = True
        self.messageWidget.destroyed.connect(self.messageWidgetRemoved)
        if self.settings.value("obsDistanceSnapping") != "no":
            self.iface.messageBar().pushWidget(self.messageWidget)

    def updateSnapperList(self, dummy=None):
        self.snapperList = []
        tolerance = self.settings.value("selectTolerance")
        units = self.settings.value("selectUnits")
        scale = self.iface.mapCanvas().mapRenderer().scale()
        for layer in self.iface.mapCanvas().layers():
            if layer.type() == QgsMapLayer.VectorLayer and layer.hasGeometryType():
                if not layer.hasScaleBasedVisibility() or layer.minimumScale() < scale <= layer.maximumScale():
                    snapLayer = QgsSnapper.SnapLayer()
                    snapLayer.mLayer = layer
                    snapLayer.mSnapTo = QgsSnapper.SnapToVertex
                    snapLayer.mTolerance = tolerance
                    if units == "map":
                        snapLayer.mUnitType = QgsTolerance.MapUnits
                    else:
                        snapLayer.mUnitType = QgsTolerance.Pixels
                    self.snapperList.append(snapLayer)

    def deactivate(self):
        self.iface.messageBar().popWidget(self.messageWidget)
        self.rubber.reset()
        self.mapCanvas.layersChanged.disconnect(self.updateSnapperList)
        self.mapCanvas.scaleChanged.disconnect(self.updateSnapperList)
        QgsMapTool.deactivate(self)

    def messageWidgetRemoved(self):
        self.messageWidgetExist = False

    def displaySnapInfo(self, snappingResults):
        if not self.messageWidgetExist:
            return
        nSnappingResults = len(snappingResults)
        if nSnappingResults == 0:
            message = "No snap"
        else:
            message = "Snapped to: <b>%s" % snappingResults[0].layer.name() + "</b>"
            if nSnappingResults > 1:
                layers = []
                message += " Nearby: "
                for res in snappingResults[1:]:
                    layerName = res.layer.name()
                    if layerName not in layers:
                        message += res.layer.name() + ", "
                        layers.append(layerName)
                message = message[:-2]
        if self.messageWidgetExist:
            self.messageWidget.setText(message)

    def canvasMoveEvent(self, mouseEvent):
        snappedPoint = self.snapToLayers(mouseEvent.pos())
        if snappedPoint is None:
            self.rubber.reset()
        else:
            self.rubber.setToGeometry(QgsGeometry().fromPoint(snappedPoint), None)

    def canvasPressEvent(self, mouseEvent):
        if mouseEvent.button() != Qt.LeftButton:
            return
        pixPoint = mouseEvent.pos()
        mapPoint = self.toMapCoordinates(pixPoint)
        #snap to layers
        mapPoint = self.snapToLayers(pixPoint, mapPoint)
        self.rubber.setToGeometry(QgsGeometry().fromPoint(mapPoint), None)
        distance = Distance(self.iface, mapPoint, 1)
        dlg = DistanceDialog(distance, self.mapCanvas)
        if dlg.exec_():
            distance.save()
        self.rubber.reset()

    def snapToLayers(self, pixPoint, initPoint=None):
        self.snapping = self.settings.value("obsDistanceSnapping")

        if self.snapping == "no":
            return initPoint

        if self.snapping == "project":
            ok, snappingResults = QgsMapCanvasSnapper(self.mapCanvas).snapToBackgroundLayers(pixPoint, [])
            self.displaySnapInfo(snappingResults)
            if ok == 0 and len(snappingResults) > 0:
                return QgsPoint(snappingResults[0].snappedVertex)
            else:
                return initPoint

        if self.snapping == "all":
            if len(self.snapperList) == 0:
                return initPoint
            snapper = QgsSnapper(self.mapCanvas.mapRenderer())
            snapper.setSnapLayers(self.snapperList)
            snapper.setSnapMode(QgsSnapper.SnapWithResultsWithinTolerances)
            ok, snappingResults = snapper.snapPoint(pixPoint, [])
            self.displaySnapInfo(snappingResults)
            if ok == 0 and len(snappingResults) > 0:
                return QgsPoint(snappingResults[0].snappedVertex)
            else:
                return initPoint
Example #40
0
class FinderBox(QComboBox):

    running = False
    toFinish = 0

    searchStarted = pyqtSignal()
    searchFinished = pyqtSignal()

    def __init__(self, finders, iface, parent=None):
        self.iface = iface
        self.mapCanvas = iface.mapCanvas()
        self.rubber = QgsRubberBand(self.mapCanvas)
        self.rubber.setColor(QColor(255, 255, 50, 200))
        self.rubber.setIcon(self.rubber.ICON_CIRCLE)
        self.rubber.setIconSize(15)
        self.rubber.setWidth(4)
        self.rubber.setBrushStyle(Qt.NoBrush)

        QComboBox.__init__(self, parent)
        self.setEditable(True)
        self.setInsertPolicy(QComboBox.InsertAtTop)
        self.setMinimumHeight(27)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.insertSeparator(0)
        self.lineEdit().returnPressed.connect(self.search)

        self.resultView = QTreeView()
        self.resultView.setHeaderHidden(True)
        self.resultView.setMinimumHeight(300)
        self.resultView.activated.connect(self.itemActivated)
        self.resultView.pressed.connect(self.itemPressed)
        self.setView(self.resultView)

        self.resultModel = ResultModel(self)
        self.setModel(self.resultModel)

        self.finders = finders
        for finder in self.finders.values():
            finder.resultFound.connect(self.resultFound)
            finder.limitReached.connect(self.limitReached)
            finder.finished.connect(self.finished)

        self.clearButton = QPushButton(self)
        self.clearButton.setIcon(
            QIcon(":/plugins/quickfinder/icons/draft.svg"))
        self.clearButton.setText('')
        self.clearButton.setFlat(True)
        self.clearButton.setCursor(QCursor(Qt.ArrowCursor))
        self.clearButton.setStyleSheet('border: 0px; padding: 0px;')
        self.clearButton.clicked.connect(self.clear)

        layout = QHBoxLayout(self)
        self.setLayout(layout)
        layout.addStretch()
        layout.addWidget(self.clearButton)
        layout.addSpacing(20)

        buttonSize = self.clearButton.sizeHint()
        # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
        padding = buttonSize.width()  # + frameWidth + 1
        self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' %
                                      padding)

    def __del__(self):
        if self.rubber:
            self.iface.mapCanvas().scene().removeItem(self.rubber)
            del self.rubber

    def clearSelection(self):
        self.resultModel.setSelected(None, self.resultView.palette())
        self.rubber.reset()

    def clear(self):
        self.clearSelection()
        self.resultModel.clearResults()
        self.lineEdit().setText('')

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.clearSelection()
        QComboBox.keyPressEvent(self, event)

    def search(self):
        if self.running:
            return

        toFind = self.lineEdit().text()
        if not toFind or toFind == '':
            return

        self.running = True
        self.searchStarted.emit()

        self.clearSelection()
        self.resultModel.clearResults()
        self.resultModel.truncateHistory(MySettings().value("historyLength"))
        self.resultModel.setLoading(True)
        self.showPopup()

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

        self.findersToStart = []
        for finder in self.finders.values():
            if finder.activated():
                self.findersToStart.append(finder)

        bbox = self.mapCanvas.fullExtent()

        while len(self.findersToStart) > 0:
            finder = self.findersToStart[0]
            self.findersToStart.remove(finder)
            self.resultModel.addResult(finder.name)
            finder.start(toFind, bbox=bbox)

        # For case there is no finder activated
        self.finished(None)

    def stop(self):
        self.findersToStart = []
        for finder in self.finders.values():
            if finder.isRunning():
                finder.stop()
        self.finished(None)

    def resultFound(self, finder, layername, value, geometry, srid):
        self.resultModel.addResult(finder.name, layername, value, geometry,
                                   srid)
        self.resultView.expandAll()

    def limitReached(self, finder, layername):
        self.resultModel.addEllipsys(finder.name, layername)

    def finished(self, finder):
        if len(self.findersToStart) > 0:
            return
        for finder in self.finders.values():
            if finder.isRunning():
                return

        self.running = False
        self.searchFinished.emit()

        self.resultModel.setLoading(False)

        QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents)

    def itemActivated(self, index):
        item = self.resultModel.itemFromIndex(index)
        self.showItem(item)

    def itemPressed(self, index):
        item = self.resultModel.itemFromIndex(index)
        if QApplication.mouseButtons() == Qt.LeftButton:
            self.showItem(item)

    def showItem(self, item):
        if isinstance(item, ResultItem):
            self.resultModel.setSelected(item, self.resultView.palette())
            geometry = self.transformGeom(item)
            self.rubber.reset(geometry.type())
            self.rubber.setToGeometry(geometry, None)
            self.zoomToRubberBand()
            return

        if isinstance(item, GroupItem):
            child = item.child(0)
            if isinstance(child, ResultItem):
                self.resultModel.setSelected(item, self.resultView.palette())
                self.rubber.reset(child.geometry.type())
                for i in xrange(0, item.rowCount()):
                    geometry = self.transformGeom(item.child(i))
                    self.rubber.addGeometry(geometry, None)
                self.zoomToRubberBand()
            return

        if item.__class__.__name__ == 'QStandardItem':
            self.clearSelection()

    def transformGeom(self, item):
        src_crs = QgsCoordinateReferenceSystem()
        src_crs.createFromSrid(item.srid)
        dest_crs = self.mapCanvas.mapRenderer().destinationCrs()
        geom = QgsGeometry(item.geometry)
        geom.transform(QgsCoordinateTransform(src_crs, dest_crs))
        return geom

    def zoomToRubberBand(self):
        geom = self.rubber.asGeometry()
        if geom:
            rect = geom.boundingBox()
            rect.scale(1.5)
            self.mapCanvas.setExtent(rect)
            self.mapCanvas.refresh()
Example #41
0
class DistanceMapTool(QgsMapTool):
    def __init__(self, iface):
        self.iface = iface
        self.line_layer = None
        self.settings = MySettings()
        QgsMapTool.__init__(self, iface.mapCanvas())

    def activate(self):
        QgsMapTool.activate(self)
        self.line_layer = MemoryLayers(self.iface).line_layer
        self.rubber = QgsRubberBand(self.canvas(), QGis.Point)
        self.rubber.setColor(self.settings.value("rubberColor"))
        self.rubber.setIcon(self.settings.value("rubberIcon"))
        self.rubber.setIconSize(self.settings.value("rubberSize"))
        self.messageWidget = self.iface.messageBar().createMessage("Intersect It", "Not snapped.")
        self.messageWidgetExist = True
        self.messageWidget.destroyed.connect(self.messageWidgetRemoved)

    def deactivate(self):
        self.iface.messageBar().popWidget(self.messageWidget)
        self.rubber.reset()
        QgsMapTool.deactivate(self)

    def messageWidgetRemoved(self):
        self.messageWidgetExist = False

    def displaySnapInfo(self, match=None):
        if not self.messageWidgetExist:
            return
        if match is None:
            message = "No snap"
        else:
            message = "Snapped to: <b>{}</b>".format(match.layer())
        self.messageWidget.setText(message)

    def canvasMoveEvent(self, mouseEvent):
        match = self.snap_to_vertex(mouseEvent.pos())
        self.rubber.reset(QGis.Point)
        if match.type() == QgsPointLocator.Vertex and match.layer() != self.line_layer:
            self.rubber.addPoint(match.point())
        self.displaySnapInfo(match)

    def canvasPressEvent(self, mouseEvent):
        if mouseEvent.button() != Qt.LeftButton:
            return
        match = self.snap_to_vertex(mouseEvent.pos())
        if match.type() != QgsPointLocator.Vertex and match.layer() != self.line_layer:
            point = self.toMapCoordinates(mouseEvent.pos())
        else:
            point = match.point()
        self.rubber.addPoint(point)
        distance = Distance(self.iface, point, 1)
        dlg = DistanceDialog(distance, self.canvas())
        if dlg.exec_():
            distance.save()
        self.rubber.reset()

    def snap_to_vertex(self, pos):
        """ Temporarily override snapping config and snap to vertices and edges
         of any editable vector layer, to allow selection of node for editing
         (if snapped to edge, it would offer creation of a new vertex there).
        """
        map_point = self.toMapCoordinates(pos)
        tol = QgsTolerance.vertexSearchRadius(self.canvas().mapSettings())
        snap_type = QgsPointLocator.Type(QgsPointLocator.Vertex)

        snap_layers = []
        for layer in self.canvas().layers():
            if not isinstance(layer, QgsVectorLayer):
                continue
            snap_layers.append(QgsSnappingUtils.LayerConfig(layer, snap_type, tol, QgsTolerance.ProjectUnits))

        snap_util = self.canvas().snappingUtils()
        old_layers = snap_util.layers()
        old_mode = snap_util.snapToMapMode()
        old_inter = snap_util.snapOnIntersections()
        snap_util.setLayers(snap_layers)
        snap_util.setSnapToMapMode(QgsSnappingUtils.SnapAdvanced)
        snap_util.setSnapOnIntersections(True)
        m = snap_util.snapToMap(map_point)
        snap_util.setLayers(old_layers)
        snap_util.setSnapToMapMode(old_mode)
        snap_util.setSnapOnIntersections(old_inter)
        return m
Example #42
0
class MoveTool(QgsMapToolAdvancedDigitizing):
    """
    Map tool class to move or copy an object
    """
    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapToolAdvancedDigitizing.__init__(self, iface.mapCanvas(),
                                              iface.cadDockWidget())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/move_icon.png'
        self.text = QCoreApplication.translate("VDLTools",
                                               "Move/Copy a feature")
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__findVertex = False
        self.__onMove = False
        self.__layer = None
        self.__confDlg = None
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__rubberBand = None
        self.__rubberSnap = None
        self.__newFeature = None
        self.__selectedVertex = None

    def activate(self):
        """
        When the action is selected
        """
        QgsMapToolAdvancedDigitizing.activate(self)
        if self.__layer.geometryType() == QGis.Point:
            self.setMode(self.CaptureLine)
        else:
            self.setMode(self.CaptureNone)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__cancel()
        QgsMapToolAdvancedDigitizing.deactivate(self)

    def toolName(self):
        """
        To get the tool name
        :return: tool name
        """
        return QCoreApplication.translate("VDLTools", "Move/Copy")

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def __cancel(self):
        """
        To cancel used variables
        """
        if self.__rubberBand is not None:
            self.canvas().scene().removeItem(self.__rubberBand)
            self.__rubberBand.reset()
            self.__rubberBand = None
        if self.__rubberSnap is not None:
            self.canvas().scene().removeItem(self.__rubberSnap)
            self.__rubberSnap.reset()
            self.__rubberSnap = None
        self.__isEditing = False
        self.__findVertex = False
        self.__onMove = False
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__confDlg = None
        self.__newFeature = None
        self.__selectedVertex = None
        self.__layer.removeSelection()
        if self.__layer.geometryType() == QGis.Point:
            self.setMode(self.CaptureLine)
        else:
            self.setMode(self.CaptureNone)

    def __removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                Signal.safelyDisconnect(self.__layer.editingStopped,
                                        self.stopEditing)
            else:
                Signal.safelyDisconnect(self.__layer.editingStarted,
                                        self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type() == QgsMapLayer.VectorLayer:
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    Signal.safelyDisconnect(self.__layer.editingStopped,
                                            self.stopEditing)
                else:
                    Signal.safelyDisconnect(self.__layer.editingStarted,
                                            self.startEditing)
            self.__layer = layer

            if self.__layer.geometryType() == QGis.Point:
                self.setMode(self.CaptureLine)
            else:
                self.setMode(self.CaptureNone)

            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.canvas().mapTool() == self:
                    self.__iface.actionPan().trigger()
            return
        self.action().setEnabled(False)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()
        self.__removeLayer()

    def __pointPreview(self, point):
        """
        To create a point geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        point_v2 = GeometryV2.asPointV2(self.__selectedFeature.geometry(),
                                        self.__iface)
        self.__newFeature = QgsPointV2(point.x(), point.y())
        self.__newFeature.addZValue(point_v2.z())
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Point)
        self.__rubberBand.setToGeometry(QgsGeometry(self.__newFeature.clone()),
                                        None)

    def __linePreview(self, point):
        """
        To create a line geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        line_v2, curved = GeometryV2.asLineV2(
            self.__selectedFeature.geometry(), self.__iface)
        vertex = QgsPointV2()
        line_v2.pointAt(self.__selectedVertex, vertex)
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line)
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        if isinstance(curved, (list, tuple)):
            self.__newFeature = QgsCompoundCurveV2()
            for pos in range(line_v2.nCurves()):
                curve_v2 = self.__newCurve(curved[pos], line_v2.curveAt(pos),
                                           dx, dy)
                self.__newFeature.addCurve(curve_v2)
                if pos == 0:
                    self.__rubberBand.setToGeometry(
                        QgsGeometry(curve_v2.curveToLine()), None)
                else:
                    self.__rubberBand.addGeometry(
                        QgsGeometry(curve_v2.curveToLine()), None)
        else:
            self.__newFeature = self.__newCurve(curved, line_v2, dx, dy)
            self.__rubberBand.setToGeometry(
                QgsGeometry(self.__newFeature.curveToLine()), None)

    @staticmethod
    def __newCurve(curved, line_v2, dx, dy):
        """
        To create a new moved line
        :param curved: if the line is curved
        :param line_v2: the original line
        :param dx: x translation
        :param dy: y translation
        :return: the new line
        """
        if curved:
            newCurve = QgsCircularStringV2()
        else:
            newCurve = QgsLineStringV2()
        points = []
        for pos in range(line_v2.numPoints()):
            x = line_v2.pointN(pos).x() - dx
            y = line_v2.pointN(pos).y() - dy
            pt = QgsPointV2(x, y)
            pt.addZValue(line_v2.pointN(pos).z())
            points.append(pt)
        newCurve.setPoints(points)
        return newCurve

    def __polygonPreview(self, point):
        """
        To create a polygon geometry preview (rubberBand)
        :param point: new position as mapPoint
        """
        polygon_v2, curved = GeometryV2.asPolygonV2(
            self.__selectedFeature.geometry(), self.__iface)
        vertex = polygon_v2.vertexAt(
            GeometryV2.polygonVertexId(polygon_v2, self.__selectedVertex))
        dx = vertex.x() - point.x()
        dy = vertex.y() - point.y()
        self.__newFeature = QgsCurvePolygonV2()
        self.__rubberBand = QgsRubberBand(self.canvas(), QGis.Line)
        line_v2 = self.__newCurve(curved[0], polygon_v2.exteriorRing(), dx, dy)
        self.__newFeature.setExteriorRing(line_v2)
        self.__rubberBand.setToGeometry(QgsGeometry(line_v2.curveToLine()),
                                        None)
        for num in range(polygon_v2.numInteriorRings()):
            line_v2 = self.__newCurve(curved[num + 1],
                                      polygon_v2.interiorRing(num), dx, dy)
            self.__newFeature.addInteriorRing(line_v2)
            self.__rubberBand.addGeometry(QgsGeometry(line_v2.curveToLine()),
                                          None)

    def __onConfirmCancel(self):
        """
        When the Cancel button in Move Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __onConfirmMove(self):
        """
        When the Move button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(QCoreApplication.translate(
                "VDLTools", "Geos geometry problem"),
                                                  level=QgsMessageBar.CRITICAL,
                                                  duration=0)
        self.__layer.changeGeometry(self.__selectedFeature.id(), geometry)
        self.__confDlg.accept()
        self.__cancel()

    def __onConfirmCopy(self):
        """
        When the Copy button in Move Confirm Dialog is pushed
        """
        geometry = QgsGeometry(self.__newFeature)
        if not geometry.isGeosValid():
            self.__iface.messageBar().pushMessage(QCoreApplication.translate(
                "VDLTools", "Geos geometry problem"),
                                                  level=QgsMessageBar.CRITICAL,
                                                  duration=0)
        feature = QgsFeature(self.__layer.pendingFields())
        feature.setGeometry(geometry)
        primaryKey = QgsDataSourceURI(self.__layer.source()).keyColumn()
        for field in self.__selectedFeature.fields():
            if field.name() != primaryKey:
                feature.setAttribute(
                    field.name(),
                    self.__selectedFeature.attribute(field.name()))
        if len(self.__selectedFeature.fields()) > 0 and self.__layer.editFormConfig().suppress() != \
                QgsEditFormConfig.SuppressOn:
            self.__iface.openFeatureForm(self.__layer, feature)
        else:
            self.__layer.addFeature(feature)
        self.__confDlg.accept()
        self.__cancel()

    def keyReleaseEvent(self, event):
        """
        When keyboard is pressed
        :param event: keyboard event
        """
        if event.key() == Qt.Key_Escape:
            self.__cancel()

    def cadCanvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """

        if type(event) == QMoveEvent:
            map_point = self.toMapCoordinates(event.pos())
        else:
            map_point = event.mapPoint()

        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            laySettings = QgsSnappingUtils.LayerConfig(self.__layer,
                                                       QgsPointLocator.All, 10,
                                                       QgsTolerance.Pixels)
            f_l = Finder.findClosestFeatureAt(map_point, self.canvas(),
                                              [laySettings])
            if f_l is not None and self.__lastFeatureId != f_l[0].id():
                self.__lastFeatureId = f_l[0].id()
                self.__layer.setSelectedFeatures([f_l[0].id()])
            if f_l is None:
                self.__layer.removeSelection()
                self.__lastFeatureId = None
        elif self.__findVertex:
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            closest = self.__selectedFeature.geometry().closestVertex(
                map_point)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setIcon(4)
            self.__rubberBand.setIconSize(20)
            self.__rubberBand.setToGeometry(
                QgsGeometry().fromPoint(closest[0]), None)
        elif self.__onMove:
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(map_point)
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(map_point)
            else:
                self.__pointPreview(map_point)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            self.__rubberBand.setWidth(2)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(8)
            if self.__rubberSnap is not None:
                self.__rubberSnap.reset()
            else:
                self.__rubberSnap = QgsRubberBand(self.canvas(), QGis.Point)
            self.__rubberSnap.setColor(color)
            self.__rubberSnap.setWidth(2)
            self.__rubberSnap.setIconSize(20)
            match = Finder.snap(map_point, self.canvas())
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer():
                        self.__rubberSnap.setIcon(4)
                    else:
                        self.__rubberSnap.setIcon(1)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(
                        point, self.canvas(), self)
                    if intersection is not None:
                        self.__rubberSnap.setIcon(1)
                        point = intersection
                    else:
                        self.__rubberSnap.setIcon(3)
                self.__rubberSnap.setToGeometry(QgsGeometry().fromPoint(point),
                                                None)

    def cadCanvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        if not self.__isEditing and not self.__findVertex and not self.__onMove:
            found_features = self.__layer.selectedFeatures()
            if len(found_features) > 0:
                if len(found_features) > 1:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate("VDLTools",
                                                   "One feature at a time"),
                        level=QgsMessageBar.INFO)
                    return
                self.__selectedFeature = found_features[0]
                if self.__layer.geometryType() != QGis.Point:
                    self.__iface.messageBar().pushMessage(
                        QCoreApplication.translate(
                            "VDLTools",
                            "Select vertex for moving (ESC to undo)"),
                        level=QgsMessageBar.INFO,
                        duration=3)
                    self.__findVertex = True
                    self.setMode(self.CaptureLine)
                    self.__rubberBand = QgsRubberBand(self.canvas(),
                                                      QGis.Point)
                else:
                    self.setMode(self.CaptureNone)
                    self.__onMove = True
        elif self.__findVertex:
            self.__findVertex = False
            self.setMode(self.CaptureNone)
            closest = self.__selectedFeature.geometry().closestVertex(
                event.mapPoint())
            self.__selectedVertex = closest[1]
            self.__onMove = True
        elif self.__onMove:
            self.__onMove = False
            mapPoint = event.mapPoint()
            match = Finder.snap(event.mapPoint(), self.canvas())
            if match.hasVertex() or match.hasEdge():
                mapPoint = match.point()
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(
                        mapPoint, self.canvas(), self)
                    if intersection is not None:
                        mapPoint = intersection
            self.__isEditing = True
            if self.__rubberBand is not None:
                self.__rubberBand.reset()
            if self.__layer.geometryType() == QGis.Polygon:
                self.__polygonPreview(mapPoint)
            elif self.__layer.geometryType() == QGis.Line:
                self.__linePreview(mapPoint)
            else:
                self.__pointPreview(mapPoint)
            color = QColor("red")
            color.setAlphaF(0.78)
            self.__rubberBand.setColor(color)
            if self.__layer.geometryType() != QGis.Point:
                self.__rubberBand.setWidth(2)
                self.__rubberBand.setLineStyle(Qt.DotLine)
            else:
                self.__rubberBand.setIcon(4)
                self.__rubberBand.setIconSize(20)
            self.__confDlg = MoveConfirmDialog()
            self.__confDlg.rejected.connect(self.__cancel)
            self.__confDlg.moveButton().clicked.connect(self.__onConfirmMove)
            self.__confDlg.copyButton().clicked.connect(self.__onConfirmCopy)
            self.__confDlg.cancelButton().clicked.connect(
                self.__onConfirmCancel)
            self.__confDlg.show()
Example #43
0
class IntersectionDialog(QDialog, Ui_Intersection, SettingDialog):
    def __init__(self, iface, observations, initPoint):
        QDialog.__init__(self)
        self.setupUi(self)
        self.settings = MySettings()
        SettingDialog.__init__(self, self.settings, UpdateMode.NoUpdate)
        self.processButton.clicked.connect(self.doIntersection)
        self.okButton.clicked.connect(self.accept)
        self.finished.connect(self.resetRubber)
        self.initPoint = initPoint

        self.observations = []
        self.solution = None
        self.report = ""

        self.rubber = QgsRubberBand(iface.mapCanvas(), QGis.Point)
        self.rubber.setColor(self.settings.value("rubberColor"))
        self.rubber.setIcon(self.settings.value("rubberIcon"))
        self.rubber.setIconSize(self.settings.value("rubberSize"))

        self.observationTableWidget.displayRows(observations)
        self.observationTableWidget.itemChanged.connect(self.disbaleOKbutton)
        self.doIntersection()

    def resetRubber(self, dummy=0):
        self.rubber.reset()

    def disbaleOKbutton(self):
        self.okButton.setDisabled(True)

    def doIntersection(self):
        self.observations = []
        self.solution = None
        self.report = ""
        self.rubber.reset()

        observations = self.observationTableWidget.getObservations()
        nObs = len(observations)
        if nObs < 2:
            self.reportBrowser.setText(QCoreApplication.translate("IntersectIt",
                                                                  "No intersection can be done "
                                                                  "with less than 2 observations."))
            return
        if nObs == 2:
            if observations[0]["type"] == "distance" and observations[1]["type"] == "distance":
                intersection = TwoCirclesIntersection(observations, self.initPoint)
            elif observations[0]["type"] == "orientation" and observations[1]["type"] == "orientation":
                intersection = TwoOrientationIntersection(observations)
            else:
                intersection = DistanceOrientationIntersection(observations, self.initPoint)
        else:
            maxIter = self.advancedIntersecLSmaxIteration.value()
            threshold = self.advancedIntersecLSconvergeThreshold.value()
            intersection = LeastSquares(observations, self.initPoint, maxIter, threshold)

        self.reportBrowser.setText(intersection.report)

        if intersection.solution is not None:
            self.solution = intersection.solution
            self.observations = observations
            self.report = intersection.report
            self.okButton.setEnabled(True)
            self.rubber.setToGeometry(QgsGeometry().fromPoint(self.solution), None)
Example #44
0
class Geo360Dialog(QWidget, Ui_orbitalDialog):
    """QGIS Plugin Implementation."""
    def __init__(self, iface, parent=None, featuresId=None, layer=None):
        """Constructor."""
        QDialog.__init__(self)

        self.setupUi(self)
        self.s = QSettings()

        self.plugin_path = os.path.dirname(os.path.realpath(__file__))

        QTextCodec.setCodecForCStrings(QTextCodec.codecForName("UTF-8"))
        Gui.loadLatin1Encoding()

        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        self.parent = parent

        #Orientation from image
        self.yaw = math.pi
        self.bearing = None

        self.layer = layer
        self.featuresId = featuresId

        #Restore Previous size
        self.RestoreSize()

        #defaults
        self.actualPointDx = None
        self.actualPointSx = None
        self.actualPointOrientation = None

        self.selected_features = qgsutils.getToFeature(self.canvas, self.layer,
                                                       self.featuresId)

        #Get image path
        self.current_image = self.GetImage()
        QtGui.qApp.processEvents()

        #Creamos el visor
        self.CreateViewer()
        QtGui.qApp.processEvents()

        #Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(self, u"Information: ",
                                           u"There is no associated image.",
                                           QgsMessageBar.INFO)
            self.ChangeUrlViewer(config.DEFAULT_EMPTY)
            self.setPosition()
            return

        #Set RubberBand
        self.setOrientation()
        self.setPosition()
        QtGui.qApp.processEvents()

        #Copy file to local server
        self.CopyFile(self.current_image)
        QtGui.qApp.processEvents()

    #Set Initial Yaw
    def SetInitialYaw(self):
        self.bearing = self.selected_features.attribute(config.column_yaw)
        self.view.browser.GetMainFrame().ExecuteFunction(
            "InitialYaw", self.bearing)
        return

    #Create Viewer
    def CreateViewer(self):

        qgsutils.showUserAndLogMessage(self,
                                       u"Information: ",
                                       u"Create viewer",
                                       QgsMessageBar.INFO,
                                       onlyLog=True)

        self.view = CefWidget(self)
        self.m_vbox = QVBoxLayout()
        self.m_vbox.addWidget(self.view)
        QtGui.qApp.processEvents()

        self.frame.setLayout(self.m_vbox)
        QtGui.qApp.processEvents()
        self.view.embed()
        QtGui.qApp.processEvents()

        return

    #Copy Image File in Local Server
    def CopyFile(self, src):

        qgsutils.showUserAndLogMessage(self,
                                       u"Information: ",
                                       u"Copiar imagem",
                                       QgsMessageBar.INFO,
                                       onlyLog=True)

        pattern = "^(?P<photo_id>\d+)[^\d].*jpg$"
        src_dir = src
        dst_dir = self.plugin_path + "\\viewer"

        #Delete images on first time
        for root, dirs, files in os.walk(dst_dir):
            for file in filter(lambda x: re.match(pattern, x), files):
                os.remove(os.path.join(root, file))

        #Copy image in local folder
        dst_dir = dst_dir + "\\image.jpg"
        shutil.copy(src_dir, dst_dir)
        QtGui.qApp.processEvents()
        return

    #Restore Dialog Size
    def RestoreSize(self):
        dw = self.s.value("EquirectangularViewer/width")
        dh = self.s.value("EquirectangularViewer/height")

        if dw == None: return
        size = self.size()

        anim = QtCore.QPropertyAnimation(self, 'size', self)
        anim.setStartValue(size)
        anim.setEndValue(QtCore.QSize(dw, dh))
        anim.setDuration(1)
        anim.start()
        QtGui.qApp.processEvents()
        return

    #Save Dialog Size
    def SaveSize(self):
        dw = self.width()
        dh = self.height()
        self.s.setValue("EquirectangularViewer/width", dw)
        self.s.setValue("EquirectangularViewer/height", dh)
        QtGui.qApp.processEvents()
        return

    #Get Selected Image
    def GetImage(self):
        try:
            path = qgsutils.getAttributeFromFeature(self.selected_features,
                                                    config.column_name)
        except:
            qgsutils.showUserAndLogMessage(self, u"Information: ",
                                           u"Column not found.",
                                           QgsMessageBar.INFO)
            return

        qgsutils.showUserAndLogMessage(self,
                                       u"Information: ",
                                       str(path),
                                       QgsMessageBar.INFO,
                                       onlyLog=True)
        return path

    #Change Url Viewer
    def ChangeUrlViewer(self, new_url):
        self.view.browser.GetMainFrame().ExecuteJavascript(
            "window.location='%s'" % new_url)
        QtGui.qApp.processEvents()
        return

    #Reaload Image viewer
    def ReloadView(self, newId):

        self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized
                            | QtCore.Qt.WindowActive)
        # this will activate the window
        self.activateWindow()
        QtGui.qApp.processEvents()

        self.selected_features = qgsutils.getToFeature(self.canvas, self.layer,
                                                       newId)

        self.current_image = self.GetImage()

        #Check if image exist
        if os.path.exists(self.current_image) is False:
            qgsutils.showUserAndLogMessage(
                self, u"Information: ", u"It is not in the associated image.",
                QgsMessageBar.INFO)
            self.ChangeUrlViewer(config.DEFAULT_EMPTY)
            self.setPosition()
            return

        #Set RubberBand
        self.setOrientation()
        self.setPosition()

        #Copy file to local server
        self.CopyFile(self.current_image)

        self.ChangeUrlViewer(config.DEFAULT_URL)
        QtGui.qApp.processEvents()

        return

    #Expanded/Decreased Dialog
    def ResizeDialog(self):

        sender = QObject.sender(self)

        w = self.width()
        h = self.height()

        size = self.size()
        anim = QtCore.QPropertyAnimation(self, 'size', self)
        anim.setStartValue(size)

        if sender.objectName() == "btn_ZoomOut":
            anim.setEndValue(QtCore.QSize(w - 50, h - 50))
        else:
            anim.setEndValue(QtCore.QSize(w + 50, h + 50))

        anim.setDuration(300)
        anim.start()
        QtGui.qApp.processEvents()

        return

    #Get to Back Image
    def GetBackNextImage(self):

        qgsutils.removeAllHighlightFeaturesFromCanvasScene(self.canvas)

        sender = QObject.sender(self)

        lys = self.canvas.layers()  #Check if mapa foto is loaded
        if len(lys) == 0:
            qgsutils.showUserAndLogMessage(
                self, u"Information: ", u"You need to upload the photo layer.",
                QgsMessageBar.INFO)
            return

        for layer in lys:
            if layer.name() == config.layer_name:
                self.encontrado = True
                self.iface.setActiveLayer(layer)
                QtGui.qApp.processEvents()

                f = self.selected_features

                ac_lordem = f.attribute(config.column_order)

                if sender.objectName() == "btn_back":
                    new_lordem = int(ac_lordem) - 1
                else:
                    new_lordem = int(ac_lordem) + 1

                #Filter mapa foto layer
                ids = [
                    feat.id() for feat in layer.getFeatures(QgsFeatureRequest(
                    ).setFilterExpression("order ='" + str(new_lordem) + "'"))
                ]

                if len(ids) == 0:
                    qgsutils.showUserAndLogMessage(
                        self, u"Information: ",
                        u"There is no superiority that follows.",
                        QgsMessageBar.INFO)
                    #Filter mapa foto layer
                    ids = [
                        feat.id()
                        for feat in layer.getFeatures(QgsFeatureRequest(
                        ).setFilterExpression("order ='" + str(ac_lordem) +
                                              "'"))
                    ]
                    #Update selected feature
                    self.ReloadView(ids[0])
                    return

                self.ReloadView(ids[0])
                QtGui.qApp.processEvents()

        if self.encontrado == False:
            qgsutils.showUserAndLogMessage(
                self, u"Information: ", u"You need to upload the photo layer.",
                QgsMessageBar.INFO)

        return

    #FullScreen action button
    def FullScreen(self, bool):
        qgsutils.showUserAndLogMessage(self,
                                       u"Information: ",
                                       u"Fullscreen.",
                                       QgsMessageBar.INFO,
                                       onlyLog=True)
        if (bool): self.showFullScreen()
        else: self.showNormal()
        QtGui.qApp.processEvents()
        return

    @staticmethod
    def ActualOrientation(yaw):
        geo360Plugin = qgis.utils.plugins["EquirectangularViewer"]
        if geo360Plugin is not None:
            geo360Dialog = qgis.utils.plugins["EquirectangularViewer"].dlg
            if geo360Dialog is not None:
                geo360Dialog.UpdateOrientation(yaw=float(yaw))
        return

    #Update Orientation
    def UpdateOrientation(self, yaw=None):
        self.bearing = self.selected_features.attribute(config.column_yaw)
        try:
            self.actualPointOrientation.reset()
        except:
            pass

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QGis.Line)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)
        self.actualPointOrientation.addPoint(self.actualPointDx)

        #End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPoint(A1x, A1y))
        #Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()

        self.actualPointOrientation.setToGeometry(
            self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle),
            self.dumLayer)
        QtGui.qApp.processEvents()

    #Set Orientation in the firt time
    def setOrientation(self, yaw=None):

        self.bearing = self.selected_features.attribute(config.column_yaw)

        self.actualPointDx = self.selected_features.geometry().asPoint()

        try:
            self.actualPointOrientation.reset()
        except:
            pass

        QtGui.qApp.processEvents()

        self.actualPointOrientation = QgsRubberBand(self.iface.mapCanvas(),
                                                    QGis.Line)
        self.actualPointOrientation.setColor(Qt.blue)
        self.actualPointOrientation.setWidth(5)

        self.actualPointOrientation.addPoint(self.actualPointDx)

        #End Point
        CS = self.canvas.mapUnitsPerPixel() * 25
        A1x = self.actualPointDx.x() - CS * math.cos(math.pi / 2)
        A1y = self.actualPointDx.y() + CS * math.sin(math.pi / 2)

        self.actualPointOrientation.addPoint(QgsPoint(A1x, A1y))
        #Vision Angle
        if yaw is not None:
            angle = float(self.bearing + yaw) * math.pi / -180
        else:
            angle = float(self.bearing) * math.pi / -180

        tmpGeom = self.actualPointOrientation.asGeometry()

        self.rotateTool = transformGeometry()
        self.dumLayer = QgsVectorLayer("Point?crs=EPSG:4326",
                                       "temporary_points", "memory")
        self.actualPointOrientation.setToGeometry(
            self.rotateTool.rotate(tmpGeom, self.actualPointDx, angle),
            self.dumLayer)
        QtGui.qApp.processEvents()

    #Set RubberBand Position
    def setPosition(self):

        self.actualPointDx = self.selected_features.geometry().asPoint()

        try:
            self.positionDx.reset()
            self.positionSx.reset()
            self.positionInt.reset()
        except:
            pass
        QtGui.qApp.processEvents()

        self.positionDx = QgsRubberBand(self.iface.mapCanvas(), QGis.Point)
        self.positionDx.setWidth(6)
        self.positionDx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionDx.setIconSize(6)
        self.positionDx.setColor(Qt.black)
        self.positionSx = QgsRubberBand(self.iface.mapCanvas(), QGis.Point)
        self.positionSx.setWidth(5)
        self.positionSx.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionSx.setIconSize(4)
        self.positionSx.setColor(Qt.blue)
        self.positionInt = QgsRubberBand(self.iface.mapCanvas(), QGis.Point)
        self.positionInt.setWidth(5)
        self.positionInt.setIcon(QgsRubberBand.ICON_CIRCLE)
        self.positionInt.setIconSize(3)
        self.positionInt.setColor(Qt.white)

        QtGui.qApp.processEvents()

        self.positionDx.addPoint(self.actualPointDx)
        self.positionSx.addPoint(self.actualPointDx)
        self.positionInt.addPoint(self.actualPointDx)

        QtGui.qApp.processEvents()

    #Close dialog
    def closeEvent(self, evt):
        qgsutils.showUserAndLogMessage(self,
                                       u"Information: ",
                                       u"Close dialog",
                                       QgsMessageBar.INFO,
                                       onlyLog=True)
        qgsutils.removeAllHighlightFeaturesFromCanvasScene(self.canvas)
        QtGui.qApp.processEvents()

        self.canvas.refresh()
        self.iface.actionPan().trigger()
        self.SaveSize()
        self.stopTimer()
        QtGui.qApp.processEvents()
        return
Example #45
0
class ExtrapolateTool(QgsMapTool):
    """
    Map tool class to extrapolate the elevation of a vertex at the end of a line
    """

    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/extrapolate_icon.png'
        self.text = QCoreApplication.translate("VDLTools",
                                                 "Extrapolate the elevation of a vertex and a "
                                                 "point at the extremity of a line")
        self.__layer = None
        self.setCursor(Qt.ArrowCursor)
        self.__isEditing = False
        self.__lastFeatureId = None
        self.__rubber = None
        self.__confDlg = None
        self.__selectedVertex = None
        self.__elevation = None
        self.__selectedFeature = None

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__rubber = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setIconSize(20)

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__cancel()
        self.__rubber = None
        QgsMapTool.deactivate(self)

    def startEditing(self):
        """
        To set the action as enable, as the layer is editable
        """
        self.action().setEnabled(True)
        Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
        self.__layer.editingStopped.connect(self.stopEditing)

    def stopEditing(self):
        """
        To set the action as disable, as the layer is not editable
        """
        self.action().setEnabled(False)
        Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
        self.__layer.editingStarted.connect(self.startEditing)
        if self.canvas().mapTool() == self:
            self.__iface.actionPan().trigger()

    def __cancel(self):
        """
        To cancel used variables
        """
        self.__layer.removeSelection()
        if self.__rubber is not None:
            self.__rubber.reset()
        self.__lastFeatureId = None
        self.__confDlg = None
        self.__selectedFeature = None
        self.__selectedVertex = None
        self.__elevation = None
        self.__isEditing = False

    def __removeLayer(self):
        """
        To remove the current working layer
        """
        if self.__layer is not None:
            if self.__layer.isEditable():
                Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
            else:
                Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = None

    def setEnable(self, layer):
        """
        To check if we can enable the action for the selected layer
        :param layer: selected layer
        """
        if layer is not None and layer.type() == QgsMapLayer.VectorLayer\
                and QGis.fromOldWkbType(layer.wkbType()) == QgsWKBTypes.LineStringZ:
            if layer == self.__layer:
                return

            if self.__layer is not None:
                if self.__layer.isEditable():
                    Signal.safelyDisconnect(self.__layer.editingStopped, self.stopEditing)
                else:
                    Signal.safelyDisconnect(self.__layer.editingStarted, self.startEditing)
            self.__layer = layer
            if self.__layer.isEditable():
                self.action().setEnabled(True)
                self.__layer.editingStopped.connect(self.stopEditing)
            else:
                self.action().setEnabled(False)
                self.__layer.editingStarted.connect(self.startEditing)
                if self.canvas().mapTool() == self:
                    self.__iface.actionPan().trigger()
            return
        self.action().setEnabled(False)
        self.__removeLayer()

    def canvasMoveEvent(self, event):
        """
        When the mouse is moved
        :param event: mouse event
        """
        if not self.__isEditing:
            laySettings = QgsSnappingUtils.LayerConfig(self.__layer, QgsPointLocator.All, 10,
                                                       QgsTolerance.Pixels)
            f_l = Finder.findClosestFeatureAt(event.mapPoint(), self.canvas(), [laySettings])
            if f_l is not None:
                self.__lastFeatureId = f_l[0].id()
                self.__layer.setSelectedFeatures([f_l[0].id()])
                self.__rubber.reset()
                geom = f_l[0].geometry()
                index = geom.closestVertex(event.mapPoint())[1]
                line_v2, curved = GeometryV2.asLineV2(geom, self.__iface)
                num_p = line_v2.numPoints()
                if num_p > 2 and (index == 0 or index == (num_p-1)):
                    self.__rubber.setIcon(4)
                    self.__rubber.setToGeometry(QgsGeometry(line_v2.pointN(index)), None)
            else:
                self.__layer.removeSelection()
                self.__rubber.reset()
                self.__lastFeatureId = None

    def canvasReleaseEvent(self, event):
        """
        When the mouse is clicked
        :param event: mouse event
        """
        found_features = self.__layer.selectedFeatures()
        if len(found_features) > 0:
            if len(found_features) > 1:
                self.__iface.messageBar().pushMessage(
                    QCoreApplication.translate("VDLTools", "One feature at a time"), level=QgsMessageBar.INFO)
                return
            geom = found_features[0].geometry()
            self.__selectedVertex = geom.closestVertex(event.mapPoint())[1]
            line_v2, curved = GeometryV2.asLineV2(geom, self.__iface)
            num_p = line_v2.numPoints()
            if num_p > 2 and (self.__selectedVertex == 0 or self.__selectedVertex == (num_p-1)):
                pt = line_v2.pointN(self.__selectedVertex)
                if self.__selectedVertex == 0:
                    pt0 = line_v2.pointN(2)
                    pt1 = line_v2.pointN(1)
                else:
                    pt0 = line_v2.pointN(num_p-3)
                    pt1 = line_v2.pointN(num_p-2)
                big_d = Finder.sqrDistForPoints(pt0, pt1)
                small_d = Finder.sqrDistForPoints(pt1, pt)
                self.__isEditing = True
                self.__selectedFeature = found_features[0]
                self.__elevation = round(pt0.z() + (1 + old_div(small_d, big_d)) * (pt1.z() - pt0.z()), 2)
                if small_d < (old_div(big_d, 4)):
                    if pt.z() is not None and pt.z() != 0:
                        message = QCoreApplication.translate("VDLTools", "This vertex has already an elevation ") + \
                                  "(" + str(pt.z()) + ")" + \
                                  QCoreApplication.translate("VDLTools",
                                                             " do you really want to change it (new elevation : ") + \
                                  str(self.__elevation) + ") ?"
                        self.__confDlg = ExtrapolateConfirmDialog(message)
                        self.__confDlg.rejected.connect(self.__cancel)
                        self.__confDlg.okButton().clicked.connect(self.__onEditOk)
                        self.__confDlg.cancelButton().clicked.connect(self.__onEditCancel)
                        self.__confDlg.show()
                    else:
                        self.__edit()
                else:
                    message = QCoreApplication.translate("VDLTools",
                                                         "The segment is too big, do you really want "
                                                         "to extrapolate anyway ? (elevation : ") + \
                              str(self.__elevation) + ") ?"
                    self.__confDlg = ExtrapolateConfirmDialog(message)
                    self.__confDlg.rejected.connect(self.__cancel)
                    self.__confDlg.okButton().clicked.connect(self.__onEditOk)
                    self.__confDlg.cancelButton().clicked.connect(self.__onEditCancel)
                    self.__confDlg.show()

    def __onEditOk(self):
        """
        When the Ok button in Extrapolate Confirm Dialog is pushed
        """
        self.__confDlg.accept()
        self.__edit()

    def __onEditCancel(self):
        """
        When the Cancel button in Extrapolate Confirm Dialog is pushed
        """
        self.__confDlg.reject()

    def __edit(self):
        """
        To add the new extrapolate elevation
        """
        line_v2, curved = GeometryV2.asLineV2(self.__selectedFeature.geometry(), self.__iface)
        line_v2.setZAt(self.__selectedVertex, self.__elevation)
        self.__layer.changeGeometry(self.__selectedFeature.id(), QgsGeometry(line_v2))
        self.__cancel()
Example #46
0
class IntersectTool(QgsMapTool):
    """
    Map tool class to create temporary circle, with center point
    """
    def __init__(self, iface):
        """
        Constructor
        :param iface: interface
        """
        QgsMapTool.__init__(self, iface.mapCanvas())
        self.__iface = iface
        self.icon_path = ':/plugins/VDLTools/icons/intersect_icon.png'
        self.text = QCoreApplication.translate("VDLTools", "From intersection")
        self.setCursor(Qt.ArrowCursor)
        self.__lineLayerID = None
        self.__pointLayerID = None
        self.__rubber = None
        self.ownSettings = None
        self.__isEditing = False
        self.__distance = 0

    def setTool(self):
        """
        To set the current tool as this one
        """
        self.canvas().setMapTool(self)

    def activate(self):
        """
        When the action is selected
        """
        QgsMapTool.activate(self)
        self.__rubber = QgsRubberBand(self.canvas(), QGis.Point)
        color = QColor("red")
        color.setAlphaF(0.78)
        self.__rubber.setColor(color)
        self.__rubber.setIcon(4)
        self.__rubber.setIconSize(20)
        self.__rubber.setWidth(2)
        self.__distance = 6.0

    def deactivate(self):
        """
        When the action is deselected
        """
        self.__cancel()
        self.__rubber = None
        QgsMapTool.deactivate(self)

    def __cancel(self):
        """
        To cancel used variables
        """
        if self.__rubber is not None:
            self.__rubber.reset()
        self.__isEditing = False

    def __setDistanceDialog(self, mapPoint):
        """
        To create an Intersect Distance Dialog
        :param mapPoint: radius of the circle
        """
        self.__dstDlg = IntersectDistanceDialog(mapPoint)
        self.__dstDlg.rejected.connect(self.__cancel)
        self.__dstDlg.okButton().clicked.connect(self.__onDstOk)
        self.__dstDlg.cancelButton().clicked.connect(self.__onDstCancel)
        self.__dstDlg.observation().setValue(self.__distance)
        self.__dstDlg.observation().selectAll()
        self.__dstDlg.show()

    def __onDstOk(self):
        """
        When the Ok button in Intersect Distance Dialog is pushed
        """
        self.__distance = float(self.__dstDlg.observation().text())
        circle = QgsCircularStringV2()
        x = self.__dstDlg.mapPoint().x()
        y = self.__dstDlg.mapPoint().y()
        circle.setPoints([
            QgsPointV2(x + self.__distance * cos(pi / 180 * a),
                       y + self.__distance * sin(pi / 180 * a))
            for a in range(0, 361, 90)
        ])
        lineLayer = self.__lineLayer()
        lineLayer.startEditing()
        feature = QgsFeature()
        feature.setGeometry(QgsGeometry(circle))
        fields = lineLayer.pendingFields()
        feature.setFields(fields)
        fieldsNames = [fields.at(pos).name() for pos in range(fields.count())]
        if "distance" in fieldsNames:
            feature.setAttribute("distance", self.__distance)
        if "x" in fieldsNames:
            feature.setAttribute("x", self.__dstDlg.mapPoint().x())
        if "y" in fieldsNames:
            feature.setAttribute("y", self.__dstDlg.mapPoint().y())
        lineLayer.addFeature(feature)
        # lineLayer.updateExtents()
        lineLayer.commitChanges()

        # center
        pointLayer = self.__pointLayer()
        pointLayer.startEditing()
        feature = QgsFeature()
        feature.setGeometry(QgsGeometry().fromPoint(self.__dstDlg.mapPoint()))
        fields = pointLayer.pendingFields()
        feature.setFields(fields)
        pointLayer.addFeature(feature)
        pointLayer.commitChanges()

        self.__dstDlg.accept()
        self.__cancel()

    def __onDstCancel(self):
        """
        When the Cancel button in Intersect Distance Dialog is pushed
        """
        self.__dstDlg.reject()

    def canvasMoveEvent(self, mouseEvent):
        """
        When the mouse is moved
        :param mouseEvent: mouse event
        """
        if not self.__isEditing:
            self.__rubber.reset()
            match = Finder.snap(mouseEvent.mapPoint(), self.canvas())
            if match.hasVertex() or match.hasEdge():
                point = match.point()
                if match.hasVertex():
                    if match.layer():
                        self.__rubber.setIcon(4)
                    else:
                        self.__rubber.setIcon(1)
                if match.hasEdge():
                    intersection = Finder.snapCurvedIntersections(
                        point, self.canvas(), self)
                    if intersection is not None:
                        self.__rubber.setIcon(1)
                        point = intersection
                    else:
                        self.__rubber.setIcon(3)
                self.__rubber.setToGeometry(QgsGeometry().fromPoint(point),
                                            None)

    def canvasReleaseEvent(self, mouseEvent):
        """
        When the mouse is clicked
        :param mouseEvent: mouse event
        """
        if mouseEvent.button() != Qt.LeftButton:
            return
        match = Finder.snap(mouseEvent.mapPoint(), self.canvas())
        if match.hasVertex() or match.hasEdge():
            point = match.point()
            if match.hasEdge():
                intersection = Finder.snapCurvedIntersections(
                    match.point(), self.canvas(), self)
                if intersection is not None:
                    point = intersection
            self.__isEditing = True
            self.__setDistanceDialog(point)

    def __lineLayer(self):
        """
        To get the line layer to create the circle
        :return: a line layer
        """
        if self.ownSettings is not None:
            if self.ownSettings.linesLayer is not None:
                layer = self.ownSettings.linesLayer
                self.__lineLayerID = layer.id()
                return layer
        layer = QgsMapLayerRegistry.instance().mapLayer(self.__lineLayerID)
        if layer is None:
            epsg = self.canvas().mapRenderer().destinationCrs().authid()
            layer = QgsVectorLayer(
                "LineString?crs=%s&index=yes&field=distance:double&field=x:double&field=y:double"
                % epsg, "Memory Lines", "memory")
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            layer.layerDeleted.connect(self.__lineLayerDeleted)
            self.__lineLayerID = layer.id()
            if self.ownSettings is not None:
                self.ownSettings.linesLayer = layer
        else:
            self.__iface.legendInterface().setLayerVisible(layer, True)
        return layer

    def __lineLayerDeleted(self):
        """
        To deselect the line layer when it is deleted
        """
        self.lineLayerID = None

    def __pointLayer(self):
        """
        To get the point layer to create the center
        :return: a point layer
        """
        if self.ownSettings is not None:
            if self.ownSettings.pointsLayer is not None:
                layer = self.ownSettings.pointsLayer
                self.__pointLayerID = layer.id()
                return layer
        layer = QgsMapLayerRegistry.instance().mapLayer(self.__pointLayerID)
        if layer is None:
            epsg = self.canvas().mapRenderer().destinationCrs().authid()
            layer = QgsVectorLayer("Point?crs=%s&index=yes" % epsg,
                                   "Memory Points", "memory")
            QgsMapLayerRegistry.instance().addMapLayer(layer)
            layer.layerDeleted.connect(self.__pointLayerDeleted)
            self.__pointLayerID = layer.id()
            if self.ownSettings is not None:
                self.ownSettings.pointsLayer = layer
        else:
            self.__iface.legendInterface().setLayerVisible(layer, True)
        return layer

    def __pointLayerDeleted(self):
        """
        To deselect the point layer when it is deleted
        :return:
        """
        self.__pointLayerID = None
class nominatim_dlg(QDockWidget, Ui_search):

    def getHttp(self, uri, params):
        QgsApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            rq = QUrl(uri)
            q = QUrlQuery()
            for (k, v) in params.items():
                q.addQueryItem(k, v)

            rq.setQuery(q)
            req = QNetworkRequest(rq)

            try:
                reply = self.nominatim_networkAccessManager.blockingGet(req)
                resource = reply.content().data().decode('utf8')
                r = json.loads(resource)

                if (isinstance(r, list)):
                    self.populateTable(r)
                else:
                    self.populateTable([r])
            except:
                self.tableResult.clearContents()

        finally:
            QgsApplication.restoreOverrideCursor()

    def searchJson(self, params, user, options, options2):
        contents = str(options).strip()
        items = contents.split(' ')

        for (k, v) in options2.items():
            if k in ['viewbox']:
                params["bounded"] = "1"
            params[k] = v

        pairs = []
        for item in items:
            pair = item.split('=', 1)
            if (pair != [''] and pair != [] and len(pair) > 1):
                pairs.append(pair)

        for (k, v) in pairs:
            if k in ['viewbox', 'countrycodes', 'limit', 'exclude_place_ids', 'addressdetails',
                     'exclude_place_ids', 'bounded', 'routewidth',
                     'osm_type', 'osm_id'] and not(k in options2.keys()):
                params[k] = v

            if k in ['viewbox']:
                params["bounded"] = "1"

        params["polygon_text"] = "1"
        params["format"] = "json"

        uri = 'https://nominatim.openstreetmap.org/search'

        self.getHttp(uri, params)

    def findNearbyJSON(self, params, user, options):
        uri = "https://nominatim.openstreetmap.org/reverse"
        params["format"] = "json"
        self.getHttp(uri, params)

    """
     Gestion de l'évènement "leave", afin d'effacer l'objet sélectionné en sortie du dock
    """
    def eventFilter(self, obj, event):
        typ = event.type()
        if typ == event.Leave:
            try:
                self.plugin.canvas.scene().removeItem(self.rubber)
            except:
                pass

        return False

    def __init__(self, parent, plugin):
        self.plugin = plugin
        QDockWidget.__init__(self, parent)
        self.setupUi(self)

        self.btnApply.setIcon(QIcon(":plugins/nominatim/arrow_green.png"))
        self.btnMask.setIcon(QIcon(":plugins/nominatim/add_mask.png"))
        self.btnLayer.setIcon(QIcon(":plugins/nominatim/add_layer.png"))

        self.tableResult.installEventFilter(self)  # cf. eventFilter method
        self.tableResult.cellDoubleClicked.connect(self.onChoose)
        self.tableResult.cellEntered.connect(self.cellEntered)

        self.editSearch.returnPressed.connect(self.onReturnPressed)
        self.btnSearch.clicked.connect(self.onReturnPressed)
        self.btnApply.clicked.connect(self.onApply)
        self.btnHelp.clicked.connect(self.plugin.do_help)
        self.btnLocalize.clicked.connect(self.doLocalize)
        self.btnMask.clicked.connect(self.onMask)
        self.btnLayer.clicked.connect(self.onLayer)

        self.MultiPolygonLayerId = None
        self.LineLayerId = None
        self.PointLayerId = None

        try:
            self.cbExtent.setChecked(self.plugin.limitSearchToExtent)
        except:
            self.cbExtent.setChecked(self.plugin.limitSearchToExtent)

        self.currentExtent = self.plugin.canvas.extent()

        self.tableResult.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

        try:
            self.editSearch.setText(self.plugin.lastSearch)
        except:
            pass

        try:
            if self.plugin.localiseOnStartup:
                self.doLocalize()
        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

        self.nominatim_networkAccessManager = QgsNetworkAccessManager.instance()

    def cellEntered(self, row, col):
        item = self.tableResult.item(row, 0)

        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
            self.showItem(item)
        except:
            pass

    def onLayer(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doLayer(item)

    def onMask(self):
        for r in self.tableResult.selectedRanges():
            item = self.tableResult.item(r.topRow(), 0)
            self.doMask(item)

    def populateRow(self, item, idx):
        id = item['place_id']
        name = item['display_name']

        try:
            className = QApplication.translate("nominatim", item['class'], None)
        except:
            className = ""

        try:
            typeName = QApplication.translate("nominatim", item['type'], None)
        except:
            typeName = ""

        try:
            wkt = item['geotext']
        except:
            wkt = None

        try:
            osm_type = item['osm_type']
        except:
            osm_type = None

        bbox = {}
        if osm_type == "node":
            lat = item['lat']
            lng = item['lon']

            poFD = ogr.FeatureDefn("Point")
            poFD.SetGeomType(ogr.wkbPoint)

            oFLD = ogr.FieldDefn('id', ogr.OFTString)
            poFD.AddFieldDefn(oFLD)
            oFLD = ogr.FieldDefn('name', ogr.OFTString)
            poFD.AddFieldDefn(oFLD)

            ogrFeature = ogr.Feature(poFD)
            wkt = "POINT("+str(lng)+" "+str(lat)+")"
            ogrGeom = ogr.CreateGeometryFromWkt(wkt)
        else:
            try:
                bbox = item['boundingbox']

                poFD = ogr.FeatureDefn("Rectangle")
                poFD.SetGeomType(ogr.wkbPolygon)

                oFLD = ogr.FieldDefn('id', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn('name', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                if wkt is None:
                    wkt = "POLYGON(("+str(bbox[2])+" "+str(bbox[0])+", "+str(bbox[2])+" " +\
                          str(bbox[1])+", "+str(bbox[3])+" "+str(bbox[1])+", "+str(bbox[3])+" " +\
                          str(bbox[0])+", "+str(bbox[2])+" "+str(bbox[0])+"))"

                ogrGeom = ogr.CreateGeometryFromWkt(wkt)
            except:
                lat = item['lat']
                lng = item['lon']

                poFD = ogr.FeatureDefn("Point")
                poFD.SetGeomType(ogr.wkbPoint)

                oFLD = ogr.FieldDefn('id', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)
                oFLD = ogr.FieldDefn('name', ogr.OFTString)
                poFD.AddFieldDefn(oFLD)

                ogrFeature = ogr.Feature(poFD)
                wkt = "POINT("+str(lng)+" "+str(lat)+")"
                ogrGeom = ogr.CreateGeometryFromWkt(wkt)

        mapCrsWKT = self.plugin.canvas.mapSettings().destinationCrs().toWkt()

        sourceSRS = osr.SpatialReference()
        sourceSRS.ImportFromEPSG(4326)
        targetSRS = osr.SpatialReference()
        targetSRS.ImportFromWkt(str(mapCrsWKT))
        trsf = osr.CoordinateTransformation(sourceSRS, targetSRS)
        try:
            ogrGeom.Transform(trsf)
        except TypeError as e:
            QgsMessageLog.logMessage("Nominatim - transformation error. Check map projection.",
                                     "Extensions")

        ogrFeature.SetGeometry(ogrGeom)
        ogrFeature.SetFID(int(idx+1))
        ogrFeature.SetField(str('id'), str(id))
        ogrFeature.SetField(str('name'), name)

        item = QTableWidgetItem(name)
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setData(Qt.UserRole, ogrFeature)
        self.tableResult.setItem(idx, 0, item)

        itemLibelle = QTableWidgetItem(className)
        itemLibelle.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 1, itemLibelle)

        itemType = QTableWidgetItem(typeName)
        itemType.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        self.tableResult.setItem(idx, 2, itemType)

    def populateTable(self, r):
        idx = 0
        self.tableResult.clearContents()
        self.tableResult.setRowCount(len(r))
        for item in r:
            self.populateRow(item, idx)
            idx = idx+1

    def doLocalize(self):
        try:
            # center
            bbox = self.plugin.canvas.extent()
            sourceCrs = self.plugin.canvas.mapSettings().destinationCrs()
            targetCrs = QgsCoordinateReferenceSystem()
            targetCrs.createFromSrid(4326)
            xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance())
            bbox = xform.transform(bbox)

            params = {"lon": str(bbox.center().x()), "lat": str(bbox.center().y()), "zoom": "10"}
            self.findNearbyJSON(params, self.plugin.gnUsername, self.plugin.gnOptions)

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

    def onReturnPressed(self):
        try:
            txt = self.editSearch.text().strip()
            self.plugin.lastSearch = self.editSearch.text()
            self.plugin.limitSearchToExtent = (self.cbExtent.isChecked())
            options = self.plugin.gnOptions

            options2 = {}
            if self.plugin.limitSearchToExtent:
                sourceCrs = self.plugin.canvas.mapSettings().destinationCrs()
                targetCrs = QgsCoordinateReferenceSystem()
                targetCrs.createFromSrid(4326)
                xform = QgsCoordinateTransform(sourceCrs, targetCrs, QgsProject.instance())
                geom = xform.transform(self.plugin.canvas.extent())
                options2 = {'viewbox': str(geom.xMinimum()) + ',' +
                            str(geom.yMaximum()) + ',' +
                            str(geom.xMaximum()) + ',' +
                            str(geom.yMinimum())}

            params = {'q': txt, 'addressdetails': '0'}
            self.searchJson(params, self.plugin.gnUsername, options, options2)

        except Exception as e:
            for m in e.args:
                QgsMessageLog.logMessage(m, 'Extensions')
            pass

    def onChoose(self, row, col):
        item = self.tableResult.item(row, 0)
        self.go(item)

    def onApply(self):
        for item in self.tableResult.selectedItems():
            self.go(item)
            break

    def getBBox(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())

        if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint):
            mapextent = self.plugin.canvas.extent()
            ww = mapextent.width()/100
            mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

            x = geom.boundingBox().center().x()
            y = geom.boundingBox().center().y()

            ww = 50.0
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceFeet:
                ww = 150
            if mapcrs.mapUnits() == QgsUnitTypes.DistanceDegrees:
                ww = 0.0005

            bbox = QgsRectangle(x-10*ww, y-10*ww, x+10*ww, y+10*ww)
            return bbox
        else:
            bbox = geom.boundingBox()
            rubberRect = QgsRectangle(bbox.xMinimum(), bbox.yMinimum(),
                                      bbox.xMaximum(), bbox.yMaximum())
            return rubberRect

    def showItem(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())

        if (ogrFeature.GetDefnRef().GetGeomType() == ogr.wkbPoint):
            self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PointGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setIcon(self.rubber.ICON_CIRCLE)
            self.rubber.setIconSize(15)
            self.rubber.setWidth(2)
            self.rubber.setToGeometry(geom, None)
        else:
            # dont show if it is larger than the canvas
            if self.plugin.canvas.extent().contains(geom.boundingBox()):
                pass
            else:
                geom = geom.intersection(QgsGeometry.fromRect(self.plugin.canvas.extent()))

            self.rubber = QgsRubberBand(self.plugin.canvas, QgsWkbTypes.PolygonGeometry)
            self.rubber.setColor(QColor(50, 50, 255, 100))
            self.rubber.setWidth(4)
            self.rubber.setToGeometry(geom, None)

    def go(self, item, zoom=True):
        try:
            self.plugin.canvas.scene().removeItem(self.rubber)
        except:
            pass

        if zoom:
            bbox = self.getBBox(item)
            self.plugin.canvas.setExtent(bbox)

        self.plugin.canvas.refresh()
        self.showItem(item)

    def doMask(self, item):
        mapcrs = self.plugin.canvas.mapSettings().destinationCrs()

        ogrFeature = item.data(Qt.UserRole)
        layerName = "OSM "+ogrFeature.GetFieldAsString('id')
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
        if (geom.type() == QgsWkbTypes.PolygonGeometry):
            try:
                try:
                    from mask import aeag_mask
                except:
                    from mask_plugin import aeag_mask

                aeag_mask.do(mapcrs, {geom}, "Mask "+layerName)

            except:
                geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())
                toCrs = self.plugin.canvas.mapSettings().destinationCrs()

                l = max(geom.boundingBox().width(), geom.boundingBox().height())
                x = geom.boundingBox().center().x()
                y = geom.boundingBox().center().y()
                rect = QgsRectangle(x-l, y-l, x+l, y+l)  # geom.boundingBox()
                rect.scale(4)
                mask = QgsGeometry.fromRect(rect)

                mask = mask.difference(geom)

                maskLayer = QgsVectorLayer("MultiPolygon", "Mask "+layerName, "memory")
                maskLayer.setCrs(toCrs)
                QgsProject.instance().addMapLayer(maskLayer)
                pr = maskLayer.dataProvider()

                fields = QgsFields()
                fields.append(QgsField("id", QVariant.String))
                fields.append(QgsField("name",  QVariant.String))
                fet = QgsFeature()
                fet.initAttributes(2)
                fet.setGeometry(mask)
                fet.setFields(fields)
                fet.setAttribute("id", (ogrFeature.GetFieldAsString('id')))
                fet.setAttribute("name", (ogrFeature.GetFieldAsString('name')))

                pr.addAttributes(fields.toList())

                maskLayer.startEditing()
                pr.addFeatures([fet])
                maskLayer.commitChanges()
                maskLayer.updateExtents()

                # transparence, epaisseur
                renderer = maskLayer.renderer()
                s = renderer.symbol()
                s.setOpacity(0.90)
                s.setColor(QColor(255, 255, 255))
                if isinstance(s, QgsLineSymbol):
                    s.setWidth(0)

                layerTree = QgsProject.instance().layerTreeRoot().findLayer(maskLayer)
                if layerTree:
                    self.plugin.iface.layerTreeView().layerTreeModel()\
                        .refreshLayerLegend(layerTree)  # Refresh legend

            self.go(item)

    def doLayer(self, item):
        ogrFeature = item.data(Qt.UserRole)
        geom = QgsGeometry.fromWkt(ogrFeature.GetGeometryRef().ExportToWkt())

        fields = QgsFields()
        fields.append(QgsField("id", QVariant.String))
        fields.append(QgsField("name",  QVariant.String))
        fet = QgsFeature()
        fet.initAttributes(2)
        fet.setFields(fields)
        fet.setGeometry(geom)
        fet.setAttribute("id", (ogrFeature.GetFieldAsString('id')))
        fet.setAttribute("name", (ogrFeature.GetFieldAsString('name')))

        vl = None
        if not self.plugin.singleLayer:
            if geom.type() == QgsWkbTypes.PolygonGeometry:
                layerName = "OSMPlaceSearch Polygon"
                layerId = self.MultiPolygonLayerId
            if geom.type() == QgsWkbTypes.LineGeometry:
                layerName = "OSMPlaceSearch Line"
                layerId = self.LineLayerId
            if geom.type() == QgsWkbTypes.PointGeometry:
                layerName = "OSMPlaceSearch Point"
                layerId = self.PointLayerId

            vl = QgsProject.instance().mapLayer(layerId)
            if vl is not None:
                pr = vl.dataProvider()
            else:
                if geom.type() == QgsWkbTypes.PolygonGeometry:
                    vl = QgsVectorLayer("MultiPolygon", layerName, "memory")
                    self.MultiPolygonLayerId = vl.id()
                if geom.type() == QgsWkbTypes.LineGeometry:
                    vl = QgsVectorLayer("MultiLineString", layerName, "memory")
                    self.LineLayerId = vl.id()
                if geom.type() == QgsWkbTypes.PointGeometry:
                    vl = QgsVectorLayer("Point", layerName, "memory")
                    self.PointLayerId = vl.id()

                if vl is not None:
                    pr = vl.dataProvider()
                    # ajout de champs
                    pr.addAttributes(fields.toList())

                QgsProject.instance().addMapLayer(vl)
        else:
            layerName = "OSM "+ogrFeature.GetFieldAsString('id')

            # creer une nouvelle couche si n'existe pas encore
            if geom.type() == QgsWkbTypes.PolygonGeometry:
                vl = QgsVectorLayer("MultiPolygon", layerName, "memory")
            if geom.type() == QgsWkbTypes.LineGeometry:
                vl = QgsVectorLayer("MultiLineString", layerName, "memory")
            if geom.type() == QgsWkbTypes.PointGeometry:
                vl = QgsVectorLayer("Point", layerName, "memory")

            if vl is not None:
                pr = vl.dataProvider()
                # ajout de champs
                pr.addAttributes(fields.toList())

            QgsProject.instance().addMapLayer(vl)

        if vl is not None:
            vl.setProviderEncoding('UTF-8')
            vl.startEditing()
            pr.addFeatures([fet])
            vl.commitChanges()

            # mise a jour etendue de la couche
            vl.updateExtents()

            layerTree = QgsProject.instance().layerTreeRoot().findLayer(vl)
            if layerTree:
                self.plugin.iface.layerTreeView()\
                    .layerTreeModel().refreshLayerLegend(layerTree)  # Refresh legend

            self.go(item, False)