22ubx_spatialite.py
33
44Example script illustrating how to create a sqlite3
5- database with the spatialite extension enabled and load
6- geometry (lat/lon/height) data from a binary UBX NAV-PVT
7- data log into it using pyubx2.
5+ database (gnss.sqlite) with the spatialite extension
6+ enabled and load geometry (lat/lon/hmsl) data from a
7+ binary GNSS data log into it using pyubx2. Log must
8+ contain UBX NAV-PVT and/or NMEA GGA messages.
9+
10+ Usage:
11+
12+ python3 ubx_spatialite.py infile="gnssdata.log" dbpath="/home/myuser/Downloads" table="gnssdata"
813
914***********************************************************
1015NB: Although sqlite3 is a native Python 3 module,
2530and check for the entry 'load_extension on'.
2631***********************************************************
2732
28- gpsdata example table has the following fields:
33+ gnssdata example table has the following fields:
2934 pk integer
3035 geom POINTZ
3136 source text
32- time integer
37+ tow integer
3338 fixtype integer
34- difftype integer
3539 dop decimal
3640 hacc decimal
3741
4347"""
4448
4549import sqlite3 # NOTE caveat above
50+ from datetime import datetime
4651from os import environ , path
52+ from sys import argv
4753
48- from pyubx2 import ERR_LOG , UBX_PROTOCOL , UBXReader
54+ from pyubx2 import ERR_LOG , NMEA_PROTOCOL , UBX_PROTOCOL , UBXReader
4955
50- # path to input file containing binary UBX NAV-PVT data
51- INFILE = "/Users/steve/Library/CloudStorage/Dropbox/Development/workspace_vscode/pyubx2/references/logs/pygpsdata-ubxcartrip.log"
52-
53- # path to spatialite database
56+ LEAPS = 18 # tow leapseconds adjustment
5457DB = "gnss.sqlite"
55- DBPATH = path .join ("/Users/steve/Downloads/qgis" , DB )
56- TABLE = "gpsdata"
5758
5859# path to mod_spatialite module, if required
5960# SLPATH = "C:/Program Files/QGIS 3.44.1/bin"
6263SQLBEGIN = "BEGIN TRANSACTION;"
6364SQLCOMMIT = "COMMIT;"
6465
65- # CREATE SQL statement with 3D POINT
66+ # CREATE SQL statement with 3D POINT (lon, lat, hmsl)
6667SQLC1 = (
6768 SQLBEGIN
6869 + (
6970 "DROP TABLE IF EXISTS {table};"
7071 "CREATE TABLE {table} (id INTEGER PRIMARY KEY, source TEXT, "
71- "time INTEGER, fixtype INTEGER, difftype INTEGER, dop DECIMAL, hacc DECIMAL);"
72- "SELECT AddGeometryColumn('gpsdata ', 'geom', 4326, 'POINT', 'XYZ');"
73- "SELECT CreateSpatialIndex('gpsdata ', 'geom');"
72+ "tow INTEGER, fixtype INTEGER, dop DECIMAL, hacc DECIMAL);"
73+ "SELECT AddGeometryColumn('{table} ', 'geom', 4326, 'POINT', 'XYZ');"
74+ "SELECT CreateSpatialIndex('{table} ', 'geom');"
7475 )
7576 + SQLCOMMIT
7677)
7778
78- # INSERT SQL statement with 3D POINT (lon, lat, height)
79+ # INSERT SQL statement with 3D POINT
7980SQLI3D = (
80- "INSERT INTO {table} (source, time , fixtype, difftype , dop, hacc, geom) "
81- "VALUES ('{source}', {time }, {fixtype}, {difftype }, {dop}, {hacc}, "
81+ "INSERT INTO {table} (source, tow , fixtype, dop, hacc, geom) "
82+ "VALUES ('{source}', {tow }, {fixtype}, {dop}, {hacc}, "
8283 "GeomFromText('POINTZ({lon} {lat} {height})', 4326));"
8384)
8485
8586
86- def create_database (con , cur ):
87+ def fix2quality (parsed ):
88+ """
89+ Convert NAV-PVT fixType to GGA quality
90+ """
91+
92+ if parsed .carrSoln == 1 : # float
93+ return 5
94+ if parsed .carrSoln == 2 : # fixed
95+ return 4
96+ if parsed .fixType in (2 , 4 ): # 2D or DR
97+ return 1
98+ if parsed .fixType == 3 : # 3D
99+ return 2
100+ return 0
101+
102+
103+ def tim2tow (tim ):
104+ """
105+ Convert GGA time to TOW.
106+ """
107+
108+ dat = datetime .combine (datetime .now ().date (), tim )
109+ wd = (dat .weekday () - 6 ) % 7
110+ return (wd * 86400 ) + dat .hour * 3600 + dat .minute * 60 + dat .second + LEAPS
111+
112+
113+ def create_database (con , cur , table ):
87114 """
88115 Create spatial database.
89116 """
@@ -97,51 +124,86 @@ def create_database(con, cur):
97124 "Your Python installation does not currently support sqlite3 extensions"
98125 ) from err
99126 print ("Loading mod_spatialite extension" )
100- con .load_extension ("mod_spatialite" )
127+ try :
128+ con .load_extension ("mod_spatialite" )
129+ except sqlite3 .OperationalError as err :
130+ raise sqlite3 .OperationalError (
131+ "Unable to locate sqlite3 mod_spatialite extension - check PATH"
132+ ) from err
101133 print ("Initialising spatial metadata (may take a few seconds)" )
102134 con .execute ("SELECT InitSpatialMetaData();" )
103135 # create database table
104- print (f"Creating { TABLE } table" )
105- cur .executescript (SQLC1 .format (table = TABLE ))
136+ print (f"Creating { table } table" )
137+ cur .executescript (SQLC1 .format (table = table ))
106138
107139
108- def load_data (cur ):
140+ def load_data (cur , infile , table ):
109141 """
110142 Load data into database.
111143 """
112144
113145 # iterate through UBX data log
114- print (f"Loading data into { TABLE } from UBX data log" )
146+ print (f"Loading data into { table } from GNSS data log" )
115147 i = 0
116148 cur .execute (SQLBEGIN )
117- with open (INFILE , "rb" ) as stream :
118- ubr = UBXReader (stream , protfilter = UBX_PROTOCOL , quitonerror = ERR_LOG )
149+ with open (infile , "rb" ) as stream :
150+ ubr = UBXReader (
151+ stream , protfilter = UBX_PROTOCOL | NMEA_PROTOCOL , quitonerror = ERR_LOG
152+ )
119153 for _ , parsed in ubr :
120- if parsed . identity == "NAV-PVT" :
154+ if "NAV-PVT" in parsed . identity :
121155 sql = SQLI3D .format (
122- table = TABLE ,
156+ table = table ,
123157 source = parsed .identity ,
124- time = parsed .iTOW ,
125- fixtype = parsed .fixType ,
126- difftype = parsed .carrSoln ,
158+ tow = int (parsed .iTOW / 1000 ), # seconds
159+ fixtype = fix2quality (parsed ),
127160 dop = parsed .pDOP ,
128161 hacc = parsed .hAcc ,
129162 lon = parsed .lon ,
130163 lat = parsed .lat ,
131164 height = parsed .hMSL / 1000 , # meters
132165 )
166+ # print(sql)
167+ cur .execute (sql )
168+ i += 1
169+ elif "GGA" in parsed .identity :
170+ sql = SQLI3D .format (
171+ table = table ,
172+ source = parsed .identity ,
173+ tow = tim2tow (parsed .time ), # seconds
174+ fixtype = parsed .quality ,
175+ dop = parsed .HDOP ,
176+ hacc = 0 ,
177+ lon = parsed .lon ,
178+ lat = parsed .lat ,
179+ height = parsed .alt , # meters
180+ )
181+ # print(sql)
133182 cur .execute (sql )
134183 i += 1
135184 cur .execute (SQLCOMMIT )
136- print (f"{ i } records loaded into { TABLE } " )
185+ print (f"{ i } records loaded into { table } " )
137186
138187
139- if __name__ == "__main__" :
188+ def main (** kwargs ):
189+ """
190+ Main routine.
191+ """
192+
193+ infile = kwargs .get ("infile" , "gnssdata.log" )
194+ dbpath = kwargs .get ("dbpath" , "." )
195+ db = path .join (dbpath , "gnss.sqlite" )
196+ table = kwargs .get ("table" , "gnssdata" )
140197
141198 # create & connect to the database
142- print (f"Connecting to database { DBPATH } " )
143- with sqlite3 .connect (DBPATH ) as connection :
199+ print (f"Connecting to database { db } " )
200+ with sqlite3 .connect (db ) as connection :
144201 cursor = connection .cursor ()
145- create_database (connection , cursor )
146- load_data (cursor )
202+ create_database (connection , cursor , table )
203+ load_data (cursor , infile , table )
147204 print ("Complete" )
205+
206+
207+ if __name__ == "__main__" :
208+
209+ main (** dict (arg .split ("=" ) for arg in argv [1 :]))
0 commit comments