Ejemplo n.º 1
0
def getGpsConnection():
    gpsConnection = None
    port = str(QgsSettings().value("/kadas/gps_port", ""))
    gpsDetector = QgsGpsDetector(port)
    loop = QEventLoop()

    def gpsDetected(connection):
        gpsConnection = connection  # NOQA
        loop.exit()

    def gpsDetectionFailed():
        loop.exit()

    gpsDetector.detected.connect(gpsDetected)
    gpsDetector.detectionFailed.connect(gpsDetectionFailed)
    gpsDetector.advance()
    loop.exec_()

    return gpsConnection
Ejemplo n.º 2
0
class GPSService(QObject):
    gpsfixed = pyqtSignal(bool, object)
    gpsfailed = pyqtSignal()
    gpsdisconnected = pyqtSignal()
    connectionStatusChanaged = pyqtSignal(bool)

    # Connect to listen to GPS status updates.
    gpsposition = pyqtSignal(QgsPointXY, object)
    firstfix = pyqtSignal(QgsPointXY, object)

    def __init__(self):
        super(GPSService, self).__init__()
        self.isConnected = False
        self._currentport = None
        self._position = None
        self.latlong_position = None
        self.elevation = None
        self.wgs84CRS = QgsCoordinateReferenceSystem(4326)
        self.crs = None
        self.waypoint = None
        self._gpsupdate_frequency = 1.0
        self._gpsupdate_last = datetime.min
        self.gpslogfile = None
        self.gpsConn = None
        # for averaging
        self.gpspoints = []

    def __del__(self):
        if self.gpsConn:
            self.deleteLater()

        if not self.gpslogfile is None:
            self.gpslogfile.close()

    @property
    def connected(self):
        """
        Is the GPS connected
        :return: Retruns true if the GPS is connected
        """
        return self.isConnected

    @connected.setter
    def connected(self, value):
        self.isConnected = value

    @property
    def position(self) -> QgsPointXY:
        return self._position

    def log_gps(self, line):
        if not GPSLOGGING:
            # No logging for you.
            return

        if self.gpslogfile is None:
            self.gpslogfile = open(GPSLOGFILENAME, "w")

        self.gpslogfile.write(line + "\n")
        self.gpslogfile.flush()

    def gpsinfo(self, attribute):
        """
        Return information about the GPS position.
        :param attribute:
        :return:
        """
        if attribute.lower() == 'x':
            return self.position.x()
        if attribute.lower() == 'y':
            return self.position.y()
        if attribute.lower() == 'z':
            return self.elevation
        if attribute.lower() == 'portname':
            return self.currentport
        else:
            return getattr(self.gpsConn.currentGPSInformation(), attribute)

    @property
    def currentport(self):
        return 'scan' if not self._currentport else self._currentport

    @currentport.setter
    def currentport(self, value):
        self._currentport = value

    def connectGPS(self, portname):
        if not self.connected:
            self._currentport = portname
            if config.get("gps", {}).get("exp1", False):
                log("Using QgsSettings override for flow control")
                if portname == 'scan' or portname == '':
                    portname = ''

                flow = config.get("gps", {}).get("flow_control", None)
                log(f"GPS flow control is: {flow}")
                settings = QgsSettings()

                if flow:
                    if flow == "hardware":
                        settings.setValue("gps/flow_control", QSerialPort.HardwareControl, QgsSettings.Core)
                    elif flow == "software":
                        settings.setValue("gps/flow_control", QSerialPort.SoftwareControl, QgsSettings.Core)

                self.detector = QgsGpsDetector(portname)
                self.detector.detected.connect(self.gpsfound_handler)
                self.detector.detectionFailed.connect(self.gpsfailed)
                self.detector.advance()
            else:
                log("Using default code for GPS connection")
                if portname == 'scan' or portname == '':
                    portname = ''
                    self.detector = QgsGpsDetector(portname)
                    self.detector.detected.connect(self.gpsfound_handler)
                    self.detector.detectionFailed.connect(self.gpsfailed)
                    self.detector.advance()
                elif portname.startswith("COM"):
                    flow = config.get("gps", {}).get("flow_control", None)
                    rate = config.get("gps", {}).get("rate", None)
                    if rate:
                        baudrates = [rate]
                    else:
                        baudrates = [QSerialPort.Baud4800,
                                     QSerialPort.Baud9600,
                                     QSerialPort.Baud38400,
                                     QSerialPort.Baud57600,
                                     QSerialPort.Baud115200]

                    for rate in baudrates:
                        device = QSerialPort(portname)
                        device.setBaudRate(rate)
                        if flow == "hardware":
                            log("Using hardware flow control")
                            device.setFlowControl(QSerialPort.HardwareControl)
                        elif flow == "software":
                            log("Using software flow control")
                            device.setFlowControl(QSerialPort.SoftwareControl)
                        else:
                            device.setFlowControl(QSerialPort.NoFlowControl)
                        device.setParity(QSerialPort.NoParity)
                        device.setDataBits(QSerialPort.Data8)
                        device.setStopBits(QSerialPort.OneStop)
                        if device.open(QIODevice.ReadWrite):
                            gpsConn = QgsNmeaConnection(device)
                            self.gpsfound_handler(gpsConn)
                            break
                        else:
                            continue
                    else:
                        # If we can't connect to any GPS just error out
                        self.gpsfailed.emit()

    def disconnectGPS(self):
        if self.connected:
            self.gpsConn.close()
            self.gpsConn.stateChanged.disconnect(self.gpsStateChanged)
            self.gpsConn.nmeaSentenceReceived.disconnect(self.parse_data)
            del self.gpsConn
            self.gpsConn = None

        self.isConnected = False
        self._position = None
        self.gpsdisconnected.emit()

    def gpsfound_handler(self, gpsConnection) -> None:
        """
        Handler for when the GPS signal has been found
        :param gpsConnection:  The connection for the GPS
        :return: None
        """
        if not isinstance(gpsConnection, QgsNmeaConnection):
            # This can be hit if the scan is used as the bindings in QGIS aren't
            # right and will not have the type set correctly
            import sip
            gpsConnection = sip.cast(gpsConnection, QgsGpsConnection)

        self.gpsConn = gpsConnection
        self.gpsConn.stateChanged.connect(self.gpsStateChanged)
        self.gpsConn.nmeaSentenceReceived.connect(self.parse_data)
        self.connected = True

    def parse_data(self, datastring):
        self.log_gps(datastring)

    def gpsStateChanged(self, gpsInfo: QgsGpsInformation):
        # TODO This can also be removed after checking the new QGIS 3 API
        if gpsInfo.fixType == NMEA_FIX_BAD or gpsInfo.status == 0 or gpsInfo.quality == 0:
            self.gpsfixed.emit(False, gpsInfo)
            return

        elif gpsInfo.fixType == NMEA_FIX_3D or NMEA_FIX_2D:
            self.gpsfixed.emit(True, gpsInfo)

        map_pos = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude)
        self.latlong_position = map_pos

        if self.crs:
            transform = QgsCoordinateTransform(self.wgs84CRS, self.crs, QgsProject.instance())
            try:
                map_pos = transform.transform(map_pos)
            except QgsCsException:
                log("Transform exception")
                return

            if self.position is None:
                self.firstfix.emit(map_pos, gpsInfo)

            if (datetime.now() - self._gpsupdate_last).total_seconds() > self._gpsupdate_frequency:
                self.gpsposition.emit(map_pos, gpsInfo)
                self._gpsupdate_last = datetime.now()
                # --- averaging -----------------------------------------------
                # if turned on
                if roam.config.settings.get('gps_averaging', True):
                    # if currently happening
                    if roam.config.settings.get('gps_averaging_in_action', True):
                        self.gpspoints.append(map_pos)
                        roam.config.settings['gps_averaging_measurements'] = len(self.gpspoints)
                        roam.config.save()
                    else:
                        if self.gpspoints: self.gpspoints = []
                # -------------------------------------------------------------
            self._position = map_pos
            self.elevation = gpsInfo.elevation

    # --- averaging func ------------------------------------------------------
    def _average(self, data, function=statistics.median):
        if not data:
            return 0, 0
        x, y = zip(*data)
        return function(x), function(y)

    def average_func(self, gps_points, average_kwargs={}):
        return self._average(
            tuple((point.x(), point.y()) for point in gps_points),
            **average_kwargs
        )
Ejemplo n.º 3
0
class GPS(QObject):
    def __init__(self, gtomain):
        super(GPS, self).__init__()
        self.gtomain = gtomain
        self.debug = self.gtomain.debug
        self.helper = self.gtomain.helper
        self.iface = self.gtomain.iface
        self.info = self.gtomain.info
        self.prj = None
        self.canvas = self.iface.mapCanvas()
        self.gpsLog = Info(self)
        self.gpsLog.panel_name = "GTO-GPS"
        self.mouse = None
        self.LastMapTool = None
        try:
            # settings
            self.settings = None
            self.timer_intervall = 1000
            self.port = None
            self.pdop = 0
            self.wgs_crs = 'EPSG:4326'
            self.gps_streaming_distance = 10

            # refs
            self.gpsCon = None
            self.gpsDetector = None
            self.gps_active = False
            self.dic_gpsinfo = {}

            self.prj_crs = None
            self.src_crs = None
            self.transformation = None
            self.marker = None
            self.prevPointXY = None
            self.prevTime = None
            self.lastGpsInfo = None
            self.center = False
            self.scale = 0
            # actions
            mw = self.iface.mainWindow()
            self.actionGPStoggle = QAction("GTO-GPS", mw)
            self.actionGPStoggle.setObjectName('mActionGTOgpsToggle')
            self.actionGPStoggle.setToolTip('GTO GPS starten')
            self.actionGPStoggle.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsToggle.png'))
            self.actionGPStoggle.setCheckable(True)
            self.actionGPStoggle.setChecked(False)
            self.actionGPStoggle.toggled.connect(self.activate)

            self.actionGPSaddPoint = QAction("GPS Punkt hinzufügen", mw)
            self.actionGPSaddPoint.setObjectName('mActionGTOgpsAddPoint')
            self.actionGPSaddPoint.setToolTip('GPS Punkt hinzufügen')
            self.actionGPSaddPoint.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsAddPoint.png'))
            self.actionGPSaddPoint.triggered.connect(self.addPoint)
            self.actionGPSaddPoint.setEnabled(False)

            self.actionGPSclick = QAction("GPS Punkt hinzufügen", mw)
            self.actionGPSclick.setObjectName('mActionGTOgpsAddcPoint')
            self.actionGPSclick.setToolTip('GPS Punkt hinzufügen')
            self.actionGPSclick.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsAddcPoint.png'))
            self.actionGPSclick.triggered.connect(self.gps_click)
            self.actionGPSclick.setEnabled(False)

            self.actionGPSstreaming = QAction("GPS streaming", mw)
            self.actionGPSstreaming.setObjectName('mActionGTOgpsStream')
            self.actionGPSstreaming.setToolTip('GPS Punkte aufzeichnen')
            self.actionGPSstreaming.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsStream.png'))
            self.actionGPSstreaming.setCheckable(True)
            self.actionGPSstreaming.setEnabled(False)
            self.actionGPSstreaming.toggled.connect(self.streaming)

            self.actionGPSsave = QAction("GPS Geometrie speichern", mw)
            self.actionGPSsave.setObjectName('mActionGTOgpsSave')
            self.actionGPSsave.setToolTip('GPS Geometrie speichern')
            self.actionGPSsave.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsSave.png'))
            self.actionGPSsave.triggered.connect(self.saveGeometry)
            self.actionGPSsave.setEnabled(False)

            self.actionGPSaddVertexTool = QAction("GPS AddVertexTool", mw)
            self.actionGPSaddVertexTool.setObjectName(
                'mActionGTOgpsAddVertexTool')
            self.actionGPSaddVertexTool.setToolTip('GPS add vertex to stream')
            self.actionGPSaddVertexTool.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsAddVertexTool.png'))
            self.actionGPSaddVertexTool.setCheckable(True)
            self.actionGPSaddVertexTool.toggled.connect(
                self.activateVertexTool)
            self.actionGPSaddVertexTool.setEnabled(False)
            # add vertex tool
            self.streamTool = VertexTool(self.iface, self.canvas, True)
            self.streamTool.canvasReleased.connect(self.addVertex)
            self.streamTool.isActive.connect(self.tool_isactive)

            self.actionGPSlog = QAction("GPS Log", mw)
            self.actionGPSlog.setObjectName('mActionGTOgpsLog')
            self.actionGPSlog.setToolTip('GPS events anzeigen')
            self.actionGPSlog.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsLog.png'))
            self.actionGPSlog.setCheckable(True)

            self.actionGPScenter = QAction("GPS zentriere Karte", mw)
            self.actionGPScenter.setObjectName('mActionGTOgpsCenter')
            self.actionGPScenter.setToolTip('GPS zentriere Karte')
            self.actionGPScenter.setIcon(
                self.gtomain.helper.getIcon('mActionGTOgpsCenter.png'))
            self.actionGPScenter.setCheckable(True)
            self.actionGPScenter.toggled.connect(self.activateCenter)

            self.actionGPSport = GtoWidgetGpsPort(self, mw)
            self.actionGPSport.portChanged.connect(self.setPort)

            self.canvas.currentLayerChanged.connect(self.layer_changed)
            # streaming
            self.debugXoffset = 0  # debugging
            self.debugYoffset = 0
            # rubberband
            # get selection color
            selcolor = self.canvas.selectionColor()
            mycolor = QColor(selcolor.red(), selcolor.green(), selcolor.blue(),
                             40)
            self.rb = QgsRubberBand(self.canvas)
            self.rb.setStrokeColor(QColor(255, 0, 0, 90))
            self.rb.setFillColor(mycolor)
            self.rb.setLineStyle(Qt.PenStyle(Qt.SolidLine))
            self.rb.setWidth(4)

            # watchdog
            self.watchdog = QTimer()
            self.watchdog.setInterval(2000)
            self.watchdog.timeout.connect(self.watch_dog)
            # layer
            self.streamLayer = None
            self.pointLayer = None
            self.gps_stream_mode = 0  # "0=rubber, 1=click, 2=both
            self.gps_debug = False
        except Exception as e:
            self.info.err(e)

    def watch_dog(self):
        try:
            self.watchdog.stop()
            self.gpsLog.log('Status:',
                            self.gpsCon.status())  # doesnt work properly
            self.marker.setColor(QColor(255, 0, 0))
            if self.actionGPSlog.isChecked(): self.gpsLog.log('No signal!')
            res = self.info.gtoQuestion("GPS deaktivieren? (Empfohlen)",
                                        title='Kein GPS Signal!',
                                        btns=QMessageBox.Yes | QMessageBox.No,
                                        parent=self.iface.mainWindow())
            if res == QMessageBox.Yes:
                self.deactivate()
                self.actionGPStoggle.setChecked(False)
        except Exception as e:
            self.info.err(e)

    def init(self):
        try:
            if self.debug: self.gpsLog.log('gps init')
            self.actionGPStoggle.setChecked(False)
            # qgis
            self.prj = QgsProject().instance()
            self.canvas = self.iface.mapCanvas()
            # settings
            if "GPS" in self.gtomain.settings:  # compatible
                self.settings = self.gtomain.settings["GPS"]
            else:
                self.settings = self.gtomain.settings
            # timer
            self.timer_intervall = self.settings.get("gps_timer_intervall",
                                                     5000)
            # gps
            self.port = self.settings.get("gps_port", None)
            if self.port is None:
                self.setPort(self.helper.getGlobalSetting('GpsPort'))
            self.pdop = self.settings.get("gps_pdop", 0)
            self.actionGPSlog.setChecked(self.settings.get("gps_log", False))
            watchdog_interval = self.settings.get("gps_watchdog_interval",
                                                  3000)
            self.watchdog.setInterval(watchdog_interval)
            # streaming
            self.pointLayer = self.settings.get("gps_point_layer", None)
            try:
                if self.pointLayer is not None:
                    self.pointLayer = self.prj.mapLayersByName(
                        self.pointLayer)[0]
                    geoType = self.pointLayer.geometryType()
                    if geoType != QgsWkbTypes.GeometryType.PointGeometry:
                        self.pointLayer = None
            except Exception as e:
                self.pointLayer = None
                self.info.err(e)
            self.gps_debug = self.settings.get("gps_debug", False)
            self.gps_stream_mode = self.settings.get("gps_stream_mode", 0)
            self.gps_streaming_distance = self.settings.get(
                "gps_streaming_distance", 1)
            # map
            self.center = self.settings.get("gps_center", True)
            self.actionGPScenter.setChecked(self.center)
            self.scale = self.settings.get("gps_scale", 0)
            # transformation
            self.prj_crs = self.prj.crs()
            self.wgs_crs = self.settings.get("gps_wgs_crs", 'EPSG:4326')
            self.init_transformation(self.wgs_crs)
        except Exception as e:
            self.info.err(e)

    def activateCenter(self):
        self.center = self.actionGPScenter.isChecked()

    def activate(self):
        try:
            self.deactivate()
            if self.actionGPStoggle.isChecked():
                if self.port is None:
                    self.info.msg("Kein GPS Port gesetzt!")
                    return
                self.actionGPSport.setEnabled(False)
                if self.actionGPSlog.isChecked():
                    self.gpsLog.log('gps activate')
                self.gpsDetector = QgsGpsDetector(self.port)
                self.gpsDetector.detected[QgsGpsConnection].connect(
                    self.connection_succeed)
                # self.gpsDetector.detected[QgsNmeaConnection].connect(self.connection_succeed)
                self.gpsDetector.detectionFailed.connect(
                    self.connection_failed)
                self.gpsDetector.advance()
                self.init_marker()
        except Exception as e:
            self.info.err(e)

    def setPort(self, port):
        try:
            self.port = port
            self.actionGPStoggle.setToolTip('GPS on/off (Port:{0})'.format(
                self.port))
        except Exception as e:
            self.info.err(e)

    def enableGPSfunctions(self, enabled):
        try:
            if self.iface.mainWindow() is None: return  # dummy
        except:
            # prevent: ERROR:  <class 'RuntimeError'> gto_gps.py | line: 240 | ('wrapped C/C++ object of type QgisInterface has been deleted',)
            # because QGIS is already closing
            return
        try:
            if self.iface.activeLayer() is not None:
                geoType = self.iface.activeLayer().geometryType()
                if geoType == QgsWkbTypes.GeometryType.PointGeometry:
                    self.actionGPSaddPoint.setEnabled(enabled)
                else:
                    self.actionGPSaddPoint.setEnabled(False)
            self.actionGPSclick.setEnabled(enabled)
            self.actionGPSstreaming.setEnabled(enabled)
            self.actionGPSport.setEnabled(not enabled)
        except Exception as e:
            self.info.err(e)

    def connection_succeed(self, connection):
        try:
            self.gps_active = True
            self.gpsCon = connection
            self.gpsCon.stateChanged.connect(self.status_changed)
            self.watchdog.start()
        except Exception as e:
            self.info.err(e)

    def connection_failed(self):
        if not self.gps_active:
            self.actionGPStoggle.setChecked(False)
            self.info.msg("GPS konnte nicht initialisiert werden!")
            self.info.log('GPS connection failed')
            self.enableGPSfunctions(False)

    def deactivate(self):
        try:
            if self.iface.mainWindow() is None: return  # dummy
        except:
            # prevent: ERROR:  <class 'RuntimeError'> gto_gps.py | line: 240 | ('wrapped C/C++ object of type QgisInterface has been deleted',)
            # because QGIS is already closing
            return
        try:
            if self.debug: self.info.log('gps deactivate')
            self.watchdog.stop()
            self.debugXoffset = 0
            self.debugYoffset = 0
            self.prevTime = None
            self.actionGPSstreaming.setChecked(False)
            self.enableGPSfunctions(False)
            self.prevPointXY = None
            if self.gpsCon is not None:
                if self.actionGPSlog.isChecked():
                    self.gpsLog.log('gps deactivate')
                self.gpsCon.close()
            if self.canvas is not None:
                self.canvas.scene().removeItem(self.marker)
            self.gps_active = False
        except Exception as e:
            self.info.err(e)

    def init_marker(self):
        try:
            if self.actionGPSlog.isChecked(): self.info.log('gps init_marker')
            self.marker = QgsVertexMarker(self.canvas)
            self.marker.setColor(QColor(255, 0, 0))  # (R,G,B)
            self.marker.setIconSize(10)
            self.circle = QgsVertexMarker.ICON_CIRCLE
            self.marker.setIconType(self.circle)
            #        ICON_BOX
            #        ICON_CIRCLE
            #        ICON_CROSS
            #        ICON_DOUBLE_TRIANGLE
            #        ICON_NONE
            #        ICON_X
            self.marker.setPenWidth(3)
        except Exception as e:
            self.info.err(e)

    def init_transformation(self, wgs_crs):
        try:
            self.src_crs = QgsCoordinateReferenceSystem(wgs_crs)
            self.transformation = QgsCoordinateTransform(
                self.src_crs, self.prj_crs, self.prj)
        except Exception as e:
            self.info.err(e)

    def status_changed(self, gpsInfo):
        try:
            if self.gpsCon.status() != 3: return  # 3=data received
            self.watchdog.stop()
            self.watchdog.start()
            if gpsInfo.longitude == 0: return
            valid = False
            self.dic_gpsinfo = {
                "direction": gpsInfo.direction,
                "elevation": gpsInfo.elevation,
                "fixMode": gpsInfo.fixMode,
                "fixType": gpsInfo.fixType,
                "hacc": gpsInfo.hacc,
                "satInfoComplete": gpsInfo.satInfoComplete,
                "speed": gpsInfo.speed,
                "utcDateTime": gpsInfo.utcDateTime,
                "vacc": gpsInfo.vacc,
                "status": gpsInfo.status,
                "hdop": gpsInfo.hdop,
                "vdop": gpsInfo.vdop,
                "pdop": gpsInfo.pdop,
                "latitude": gpsInfo.latitude,
                "longitude": gpsInfo.longitude,
                "satellitesInView": gpsInfo.satellitesInView,
                "satellitesUsed": gpsInfo.satellitesUsed,
                "quality": gpsInfo.quality
            }
            if self.actionGPSlog.isChecked():
                self.gpsLog.log('direction:', gpsInfo.direction)
                self.gpsLog.log('elevation:', gpsInfo.elevation)
                self.gpsLog.log('fixMode:', gpsInfo.fixMode)
                self.gpsLog.log('fixType:', gpsInfo.fixType)
                self.gpsLog.log('hacc:', gpsInfo.hacc)
                self.gpsLog.log('satInfoComplete:', gpsInfo.satInfoComplete)
                self.gpsLog.log('speed:', gpsInfo.speed)
                self.gpsLog.log('utcDateTime:', gpsInfo.utcDateTime.toString())
                self.gpsLog.log('vacc:', gpsInfo.vacc)
                self.gpsLog.log('status:', gpsInfo.status)
                self.gpsLog.log('hdop:', gpsInfo.hdop)
                self.gpsLog.log('vdop:', gpsInfo.vdop)
                self.gpsLog.log('pdop:', gpsInfo.pdop)
                self.gpsLog.log('latitude:', gpsInfo.latitude)
                self.gpsLog.log('longitude:', gpsInfo.longitude)
                self.gpsLog.log('satellitesInView:',
                                len(gpsInfo.satellitesInView))
                self.gpsLog.log('satellitesUsed:', gpsInfo.satellitesUsed)
                self.gpsLog.log('quality:', gpsInfo.quality)
                self.gpsLog.log("---------------------")
            if gpsInfo.pdop >= self.pdop:  # gps ok
                valid = True
                self.enableGPSfunctions(True)
                self.marker.setColor(QColor(0, 200, 0))
            else:

                self.enableGPSfunctions(False)
                self.marker.setColor(QColor(255, 0, 0))
            wgs84_pointXY = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude)
            wgs84_point = QgsPoint(wgs84_pointXY)

            wgs84_point.transform(self.transformation)
            if self.gps_debug:
                self.debugXoffset = self.debugXoffset + random.randint(
                    0, int(self.gps_streaming_distance))
                self.debugYoffset = self.debugYoffset + random.randint(
                    0, int(self.gps_streaming_distance))
                x = wgs84_point.x() + self.debugXoffset
                y = wgs84_point.y() + self.debugYoffset
            else:
                x = wgs84_point.x()
                y = wgs84_point.y()
            if self.actionGPSlog.isChecked():
                self.gpsLog.log("x:", x)
                self.gpsLog.log("y:", y)
                self.gpsLog.log("--------------------")
            mapPointXY = QgsPointXY(x, y)
            self.lastGpsInfo = gtoGPSinfo(gpsInfo, mapPointXY, valid)
            # map
            if self.center and valid:
                self.canvas.setCenter(mapPointXY)
                if self.scale > 0: self.canvas.zoomScale(self.scale)
            self.marker.setCenter(mapPointXY)
            self.marker.show()
            # streaming
            gpsDateTime = None
            diff = 0
            if self.pointLayer is not None:
                self.actionGPSaddPoint.setEnabled(valid)
            if self.actionGPSstreaming.isChecked(
            ) and valid and not self.actionGPSaddVertexTool.isChecked():
                if self.prevTime is None:
                    self.prevTime = gpsInfo.utcDateTime.currentDateTime()
                else:
                    gpsDateTime = gpsInfo.utcDateTime.currentDateTime()
                    diff = self.prevTime.msecsTo(gpsDateTime)
                    if diff < self.timer_intervall:
                        return

                if self.prevPointXY is None:
                    self.prevPointXY = mapPointXY
                    self.prevTime = gpsDateTime
                    if self.pointLayer is not None:
                        self.addPoint()
                    else:
                        if self.gps_stream_mode == 1:
                            self.gps_click()
                        elif self.gps_stream_mode == 2:
                            self.gps_click()
                            self.addVertex(mapPointXY)
                        else:
                            self.addVertex(mapPointXY)
                else:
                    qgsDistance = QgsDistanceArea()
                    distance = qgsDistance.measureLine(
                        [self.prevPointXY, mapPointXY])
                    if distance > self.gps_streaming_distance and diff > self.timer_intervall:
                        self.prevTime = gpsDateTime
                        self.prevPointXY = mapPointXY
                        if self.pointLayer is not None:
                            self.addPoint()
                        else:
                            if self.gps_stream_mode == 1:
                                self.gps_click()
                            elif self.gps_stream_mode == 2:
                                self.gps_click()
                                self.addVertex(mapPointXY)
                            else:
                                self.addVertex(mapPointXY)
        except Exception as e:
            self.info.err(e)

    def gps_click(self):
        from pynput.mouse import Button, Controller
        try:
            if self.gps_active:
                if self.debug: self.gpsLog.log('gps_click')
                actionAddFeature = self.iface.mainWindow().findChild(
                    QAction, 'mActionAddFeature')
                gtoGpsInfo = self.lastGpsInfo
                if gtoGpsInfo.isValid and actionAddFeature.isChecked():
                    self.mouse = Controller()
                    mapX = gtoGpsInfo.mapPointXY.x()
                    mapY = gtoGpsInfo.mapPointXY.y()
                    pointXY = self.canvas.getCoordinateTransform().transform(
                        mapX, mapY)  # map coords to device coords
                    x = pointXY.x()
                    y = pointXY.y()
                    p = self.canvas.mapToGlobal(QPoint(
                        x, y))  # device coords to screen coords
                    x = p.x()
                    y = p.y()
                    prevX, prevY = self.mouse.position
                    self.mouse.position = (x, y)
                    if self.debug: self.gpsLog.log('addPoint', x, "/", y)
                    self.mouse.click(Button.left, 1)
                    self.mouse.position = (prevX, prevY)
        except Exception as e:
            self.info.err(e)

    def layer_changed(self):
        try:
            self.actionGPSstreaming.setChecked(False)
            if self.gps_active:
                geoType = self.iface.activeLayer().geometryType()
                if geoType == QgsWkbTypes.GeometryType.PointGeometry or self.pointLayer is not None:
                    self.actionGPSaddPoint.setEnabled(True)
                else:
                    self.actionGPSaddPoint.setEnabled(False)
        except Exception as e:
            self.info.err(e)

    def streaming(self):
        try:
            self.actionGPSsave.setEnabled(False)
            self.actionGPSaddVertexTool.setChecked(False)
            self.activateVertexTool()
            if self.pointLayer is not None:
                return
            if self.actionGPSstreaming.isChecked():
                if self.streamLayer is not None:
                    self.actionGPSstreaming.setChecked(False)
                    res = self.saveGeometry()
                    if res == QMessageBox.Cancel: return
                    self.actionGPSstreaming.setChecked(True)
                geoType = self.iface.activeLayer().geometryType()
                if geoType == QgsWkbTypes.GeometryType.PolygonGeometry or geoType == QgsWkbTypes.GeometryType.LineGeometry:
                    self.streamLayer = self.iface.activeLayer()
                    self.actionGPSaddVertexTool.setEnabled(True)
                    self.rb.reset(geoType)
                else:
                    self.actionGPSstreaming.setChecked(False)
                    self.actionGPSaddVertexTool.setEnabled(False)
                    self.info.msg(
                        "Aktiver Layer muss vom Typ Polgone oder Line sein!")
            else:
                self.actionGPSaddVertexTool.setEnabled(False)
                if self.streamLayer is not None:
                    geoType = self.streamLayer.geometryType()
                    tools = self.settings.get('gps_afterstream_tools', [])
                    if geoType == QgsWkbTypes.GeometryType.PolygonGeometry:
                        if self.rb.numberOfVertices() > 2:
                            self.actionGPSsave.setEnabled(True)
                            self.gtomain.runcmd(tools)
                    if geoType == QgsWkbTypes.GeometryType.LineGeometry:
                        if self.rb.numberOfVertices() > 1:
                            self.actionGPSsave.setEnabled(True)
                            self.gtomain.runcmd(tools)
        except Exception as e:
            self.info.err(e)

    def addPoint(self):
        try:
            if self.gps_active:
                if self.actionGPSlog.isChecked(): self.gpsLog.log('addPoint')
                gtoGpsInfo = self.lastGpsInfo
                if gtoGpsInfo.isValid:
                    if self.pointLayer is not None:
                        layer = self.pointLayer
                    else:
                        layer = self.iface.activeLayer()
                    geoType = layer.geometryType()
                    if geoType != QgsWkbTypes.GeometryType.PointGeometry:
                        self.info.msg("Kein Punktlayer ausgewählt!")
                        return
                    # create feature
                    feat = QgsVectorLayerUtils.createFeature(layer)
                    tr = QgsCoordinateTransform(self.prj_crs, layer.crs(),
                                                self.prj)
                    tr_point = tr.transform(gtoGpsInfo.mapPointXY)
                    geo = QgsGeometry.fromPointXY(tr_point)
                    feat.setGeometry(geo)
                    # set attributes
                    gps_addpoint_attributes = self.settings.get(
                        "gps_addpoint_attributes", {})
                    for k, v in gps_addpoint_attributes.items():
                        feat[k] = layer.fields().field(k).convertCompatible(
                            self.dic_gpsinfo[v])
                    # add to layer
                    if self.pointLayer is not None:
                        # add to provider
                        (res,
                         outFeats) = layer.dataProvider().addFeatures([feat])
                        layer.select(outFeats[0].id())
                    else:
                        if not layer.isEditable(): layer.startEditing()
                        layer.beginEditCommand('Add stream geometry')
                        layer.addFeatures([feat])
                        layer.endEditCommand()
                        self.helper.refreshLayer(layer)
        except Exception as e:
            self.info.err(e)

    def saveGeometry(self, force=False):
        try:
            if self.streamLayer is not None:
                if not force:
                    res = self.info.gtoQuestion(
                        "GPS Stream-Geometry in Layer \n{0}\nspeichern?".
                        format(self.streamLayer.name()),
                        title='GPS Streaming',
                        btns=QMessageBox.Yes | QMessageBox.No
                        | QMessageBox.Cancel,
                        parent=self.iface.mainWindow())
                    if res == QMessageBox.Cancel: return res
                    if res == QMessageBox.No:
                        self.actionGPSsave.setEnabled(False)
                        self.streamLayer = None
                        self.rb.reset()
                        return
                # create feature
                feat = QgsVectorLayerUtils.createFeature(self.streamLayer)
                geo = self.rb.asGeometry()
                feat.setGeometry(geo)
                # add to layer
                if not self.streamLayer.isEditable():
                    self.streamLayer.startEditing()
                self.streamLayer.beginEditCommand('Add stream geometry')
                self.streamLayer.addFeatures([feat])
                self.streamLayer.endEditCommand()
                # add to provider
                # (res, outFeats) = self.streamLayer.dataProvider().addFeatures([feat])
                # self.streamLayer.select(outFeats[0].id())

                # reset streaming
                self.actionGPSsave.setEnabled(False)
                self.streamLayer = None
                self.rb.reset()
        except Exception as e:
            self.streamLayer.destroyEditCommand()
            self.info.err(e)

    def addVertex(self, point):
        try:
            self.rb.addPoint(point)
        except Exception as e:
            self.info.err(e)

    def activateVertexTool(self):
        try:
            if self.actionGPSaddVertexTool.isChecked():
                if self.actionGPSlog.isChecked():
                    self.gpsLog.log('activated VertexTool: stream paused')
                self.LastMapTool = self.canvas.mapTool()
                self.canvas.setMapTool(self.streamTool)
            else:
                if self.actionGPSlog.isChecked():
                    self.gpsLog.log('deactivated VertexTool: stream resumed')
                self.canvas.setMapTool(self.LastMapTool)
        except Exception as e:
            self.info.err(e)

    def tool_isactive(self, active):
        try:
            pass
        except Exception as e:
            self.info.err(e)
Ejemplo n.º 4
0
class GPS_Thread(QObject):

    #https://gis.stackexchange.com/questions/307209/accessing-gps-via-pyqgis

    gpsActivated = pyqtSignal(QgsGpsConnection)
    """ signal will be emitted when gps is activated"""
    gpsDeactivated = pyqtSignal()
    gpsError = pyqtSignal(Exception)
    gpsPosition = pyqtSignal(object, object)

    def __init__(self, dest_crs, gpsPort):
        self.prj = QgsProject().instance()
        self.connectionRegistry = QgsApplication.gpsConnectionRegistry()
        super(GPS_Thread, self).__init__()
        try:
            self.gps_active = False
            # set up transformation
            self.dest_crs = self.prj.crs()
            self.transformation = QgsCoordinateTransform(
                QgsCoordinateReferenceSystem("EPSG:4326"), self.dest_crs,
                QgsProject.instance())
            self.gpsCon = None
            TOMsMessageLog.logMessage(
                "In GPS_Thread.__init__ - initialised ... ", level=Qgis.Info)
            self.retry_attempts = 0
        except Exception as e:
            TOMsMessageLog.logMessage(
                ("In GPS_Thread.__init__ - exception: " + str(e)),
                level=Qgis.Warning)
            self.gpsError.emit(e)

        self.gpsPort = gpsPort

    def startGPS(self):

        try:
            TOMsMessageLog.logMessage("In GPS_Thread.startGPS - running ... ",
                                      level=Qgis.Info)
            self.gpsCon = None
            #self.port = "COM3"  # TODO: Add menu to select port
            self.gpsDetector = QgsGpsDetector(self.gpsPort)
            self.gpsDetector.detected[QgsGpsConnection].connect(
                self.connection_succeed)
            self.gpsDetector.detectionFailed.connect(self.connection_failed)

            self.gpsDetector.advance()

        except Exception as e:
            TOMsMessageLog.logMessage(
                ("In GPS_Thread.startGPS - exception: " + str(e)),
                level=Qgis.Warning)
            self.gpsError.emit(e)

    def endGPS(self):
        TOMsMessageLog.logMessage(("In GPS_Thread.endGPS ...."),
                                  level=Qgis.Warning)
        self.gps_active = False

        # shutdown the receiver
        if self.gpsCon is not None:
            self.gpsCon.close()
        TOMsMessageLog.logMessage(
            ("In GPS_Thread.status_changed - deactivating gnss ... "),
            level=Qgis.Warning)
        self.connectionRegistry.unregisterConnection(self.gpsCon)
        self.gpsDeactivated.emit()

    def connection_succeed(self, connection):
        try:
            TOMsMessageLog.logMessage(
                ("In GPS_Thread.connection_succeed - GPS connected ...."),
                level=Qgis.Warning)
            self.gps_active = True
            self.gpsCon = connection
            self.connectionRegistry.registerConnection(connection)
            self.gpsActivated.emit(connection)

            self.gpsCon.stateChanged.connect(self.status_changed)

        except Exception as e:
            TOMsMessageLog.logMessage(
                ("In GPS_Thread.connection_succeed - exception: " + str(e)),
                level=Qgis.Warning)
            self.gpsError.emit(e)
            """while self.gps_active:
            TOMsMessageLog.logMessage(
                "In GPS_Thread:connection_succeed: checking status ... {}".format(self.attempts),
                level=Qgis.Warning)
            time.sleep(1.0)
            self.attempts = self.attempts + 1
            if self.attempts > 5:
                TOMsMessageLog.logMessage(
                    ("In GPS_Thread:status_changed: problem receiving gnss position ... exiting ... "),
                    level=Qgis.Warning)
                self.endGPS()"""

    def connection_failed(self):
        TOMsMessageLog.logMessage(
            ("In GPS_Thread.connection_failed - GPS connection failed ...."),
            level=Qgis.Warning)
        self.endGPS()

    def status_changed(self, gpsInfo):
        TOMsMessageLog.logMessage(("In GPS_Thread.status_changed ...."),
                                  level=Qgis.Info)
        if self.gps_active:
            try:
                #self.retry_attempts = self.retry_attempts + 1
                if self.gpsCon.status() == 3:  #data received
                    """TOMsMessageLog.logMessage(("In GPS - fixMode:" + str(gpsInfo.fixMode)),
                                             level=Qgis.Info)
                    TOMsMessageLog.logMessage(("In GPS - pdop:" + str(gpsInfo.pdop)),
                                             level=Qgis.Info)
                    TOMsMessageLog.logMessage(("In GPS - satellitesUsed:" + str(gpsInfo.satellitesUsed)),
                                             level=Qgis.Info)
                    TOMsMessageLog.logMessage(("In GPS - longitude:" + str(gpsInfo.longitude)),
                                             level=Qgis.Info)
                    TOMsMessageLog.logMessage(("In GPS - latitude:" + str(gpsInfo.latitude)),
                                             level=Qgis.Info)
                    TOMsMessageLog.logMessage(("In GPS - ====="),
                                             level=Qgis.Info)"""
                    wgs84_pointXY = QgsPointXY(gpsInfo.longitude,
                                               gpsInfo.latitude)
                    wgs84_point = QgsPoint(wgs84_pointXY)
                    wgs84_point.transform(self.transformation)
                    x = wgs84_point.x()
                    y = wgs84_point.y()
                    mapPointXY = QgsPointXY(x, y)
                    self.gpsPosition.emit(mapPointXY, gpsInfo)
                    time.sleep(1)

                    TOMsMessageLog.logMessage(
                        ("In GPS - location:" + mapPointXY.asWkt()),
                        level=Qgis.Info)
                    self.attempts = 0
                    """else:
                    TOMsMessageLog.logMessage(("In GPS_Thread:status_changed: problem receiving gnss position ... "),
                                              level=Qgis.Info)
                    if self.retry_attempts > 5:
                        TOMsMessageLog.logMessage(("In GPS_Thread:status_changed: problem receiving gnss position ... exiting ... "),
                                                  level=Qgis.Info)
                        self.gps_active = False"""

            except Exception as e:
                TOMsMessageLog.logMessage(
                    ("In GPS_Thread.status_changed - exception: " + str(e)),
                    level=Qgis.Warning)
                self.gpsError.emit(e)
            return

    def getLocationFromGPS(self):
        TOMsMessageLog.logMessage(
            "In CreateFeatureWithGPSTool - addPointFromGPS", level=Qgis.Info)
        # assume that GPS is connected and get current co-ords ...
        GPSInfo = self.gpsCon.currentGPSInformation()
        lon = GPSInfo.longitude
        lat = GPSInfo.latitude
        TOMsMessageLog.logMessage(
            "In CreateFeatureWithGPSTool:addPointFromGPS - lat: " + str(lat) +
            " lon: " + str(lon),
            level=Qgis.Info)
        # ** need to be able to convert from lat/long to Point
        gpsPt = self.transformation.transform(QgsPointXY(lon, lat))

        #self.gpsPosition.emit(gpsPt)

        # opportunity to add details about GPS point to another table

        return gpsPt
Ejemplo n.º 5
0
class GPSService(QObject):
    gpsfixed = pyqtSignal(bool, object)
    gpsfailed = pyqtSignal()
    gpsdisconnected = pyqtSignal()

    # Connect to listen to GPS status updates.
    gpsposition = pyqtSignal(QgsPointXY, object)
    firstfix = pyqtSignal(QgsPointXY, object)

    def __init__(self):
        super(GPSService, self).__init__()
        self.isConnected = False
        self._currentport = None
        self._position = None
        self.latlong_position = None
        self.elevation = None
        self.info = QgsGpsInformation()
        self.wgs84CRS = QgsCoordinateReferenceSystem(4326)
        self.crs = None
        self.waypoint = None
        self._gpsupdate_frequency = 1.0
        self._gpsupdate_last = datetime.min
        self.gpslogfile = None
        self.gpsConn = None
        self.device = None

    def __del__(self):
        if self.gpsConn:
            self.deleteLater()

        if self.device:
            self.deleteLater()

        if not self.gpslogfile is None:
            self.gpslogfile.close()

    @property
    def position(self) -> QgsPointXY:
        return self._position

    def log_gps(self, line):
        if not GPSLOGGING:
            # No logging for you.
            return

        if self.gpslogfile is None:
            self.gpslogfile = open(GPSLOGFILENAME, "w")

        self.gpslogfile.write(line + "\n")
        self.gpslogfile.flush()

    def gpsinfo(self, attribute):
        """
        Return information about the GPS position.
        :param attribute:
        :return:
        """
        if attribute.lower() == 'x':
            return self.position.x()
        if attribute.lower() == 'y':
            return self.position.y()
        if attribute.lower() == 'z':
            return self.elevation
        if attribute.lower() == 'portname':
            return self.currentport
        else:
            return getattr(self.info, attribute)

    @property
    def currentport(self):
        return 'scan' if not self._currentport else self._currentport

    @currentport.setter
    def currentport(self, value):
        self._currentport = value

    def connectGPS(self, portname):
        if not self.isConnected:
            self._currentport = portname
            if portname == 'scan' or portname == '':
                portname = ''
                self.detector = QgsGpsDetector(portname)
                self.detector.detected.connect(self._gpsfound)
                self.detector.detectionFailed.connect(self.gpsfailed)
                self.isConnectFailed = False
                self.detector.advance()
            elif portname.startswith("COM"):
                self.device = QSerialPort(portname)
                baudrates = [
                    QSerialPort.Baud4800, QSerialPort.Baud9600,
                    QSerialPort.Baud38400, QSerialPort.Baud57600,
                    QSerialPort.Baud115200
                ]
                flow = config.get("gps", {}).get("flow_control", None)
                rate = config.get("gps", {}).get("rate", None)
                if rate:
                    baudrates = [rate]

                for rate in baudrates:
                    self.device.setBaudRate(rate)
                    if flow == "hardware":
                        log("using hardware flow control for GPS")
                        self.device.setFlowControl(QSerialPort.HardwareControl)
                    elif flow == "software":
                        log("using software flow control for GPS")
                        self.device.setFlowControl(QSerialPort.SoftwareControl)
                    else:
                        self.device.setFlowControl(QSerialPort.NoFlowControl)
                    self.device.setParity(QSerialPort.NoParity)
                    self.device.setDataBits(QSerialPort.Data8)
                    self.device.setStopBits(QSerialPort.OneStop)
                    if self.device.open(QIODevice.ReadWrite):
                        log("Connection opened with {0}".format(
                            self.device.baudRate()))
                        self.gpsConn = QgsNmeaConnection(self.device)
                        self._gpsfound(self.gpsConn)
                        break
                    else:
                        del self.device
                        continue
                else:
                    # If we can't connect to any GPS just error out
                    self.gpsfailed.emit()

    def disconnectGPS(self):
        if self.isConnected:
            self.gpsConn.close()
            self.gpsConn.stateChanged.disconnect(self.gpsStateChanged)
            # TODO: This doesn't fire for some reason anymore.  Do we need it still?
            self.gpsConn.nmeaSentenceReceived.disconnect(self.parse_data)
            self.gpsConn.deleteLater()

        self.isConnected = False
        self._position = None
        self.gpsdisconnected.emit()

    def _gpsfound(self, gpsConnection):
        if not isinstance(gpsConnection, QgsNmeaConnection):
            # This can be hit if the scan is used as the bindings in QGIS aren't
            # right and will not have the type set correctly
            import sip
            gpsConnection = sip.cast(gpsConnection, QgsGpsConnection)
            self.gpsConn = gpsConnection
        self.gpsConn.stateChanged.connect(self.gpsStateChanged)
        self.gpsConn.nmeaSentenceReceived.connect(self.parse_data)
        self.isConnected = True

        # TODO: This doesn't fire for some reason anymore.  Do we need it still?

    def parse_data(self, datastring):
        self.log_gps(datastring)
        try:
            data = pynmea2.parse(datastring)
        except AttributeError as er:
            log(er.message)
            return
        except pynmea2.SentenceTypeError as er:
            log(er.message)
            return
        except pynmea2.ParseError as er:
            log(er.message)
            return
        except pynmea2.ChecksumError as er:
            log(er.message)
            return

        mappings = {
            "RMC": self.extract_rmc,
            "GGA": self.extract_gga,
            "GSV": self.extract_gsv,
            "VTG": self.extract_vtg,
            "GSA": self.extract_gsa
        }
        try:
            mappings[data.sentence_type](data)
            self.gpsStateChanged(self.info)
        except KeyError as ex:
            log("Unhandled message type: {}. Message: {}".format(
                data.sentence_type, datastring))
            return
        except AttributeError as ex:
            log(ex)
            return

    def extract_vtg(self, data):
        self.info.speed = safe_float(data.spd_over_grnd_kmph)
        return self.info

    def extract_gsa(self, data):
        self.info.hdop = safe_float(data.hdop)
        self.info.pdop = safe_float(data.pdop)
        self.info.vdop = safe_float(data.vdop)
        self.info.fixMode = data.mode
        print(data.mode_fix_type)
        self.info.fixType = safe_int(data.mode_fix_type)
        return self.info

    def extract_gsv(self, data):
        pass

    def extract_gga(self, data):
        self.info.latitude = data.latitude
        self.info.longitude = data.longitude
        self.info.elevation = safe_float(data.altitude)
        self.info.quality = safe_int(data.gps_qual)
        self.info.satellitesUsed = safe_int(data.num_sats)
        return self.info

    def extract_rmc(self, data):
        self.info.latitude = data.latitude
        self.info.longitude = data.longitude
        self.info.speed = KNOTS_TO_KM * safe_float(data.spd_over_grnd)
        self.info.status = data.status
        self.info.direction = safe_float(data.true_course)
        if data.datestamp and data.timestamp:
            date = QDate(data.datestamp.year, data.datestamp.month,
                         data.datestamp.day)
            time = QTime(data.timestamp.hour, data.timestamp.minute,
                         data.timestamp.second)
            dt = QDateTime()
            self.info.utcDateTime.setTimeSpec(Qt.UTC)
            self.info.utcDateTime.setDate(date)
            self.info.utcDateTime.setTime(time)
        return self.info

    def gpsStateChanged(self, gpsInfo: QgsGpsInformation):
        if gpsInfo.fixType == NMEA_FIX_BAD or gpsInfo.status == 0 or gpsInfo.quality == 0:
            self.gpsfixed.emit(False, gpsInfo)
            return

        elif gpsInfo.fixType == NMEA_FIX_3D or NMEA_FIX_2D:
            self.gpsfixed.emit(True, gpsInfo)

        map_pos = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude)
        self.latlong_position = map_pos

        if self.crs:
            transform = QgsCoordinateTransform(self.wgs84CRS, self.crs,
                                               QgsProject.instance())
            try:
                map_pos = transform.transform(map_pos)
            except QgsCsException:
                log("Transform exception")
                return

            if self.position is None:
                self.firstfix.emit(map_pos, gpsInfo)

            self.info = gpsInfo

            if (datetime.now() - self._gpsupdate_last
                ).total_seconds() > self._gpsupdate_frequency:
                self.gpsposition.emit(map_pos, gpsInfo)
                self._gpsupdate_last = datetime.now()

            self._position = map_pos
            self.elevation = gpsInfo.elevation