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
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 )
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)
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
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