rda-python-icoads 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rda-python-icoads might be problematic. Click here for more details.
- rda_python_icoads/PgIMMA.py +1785 -0
- rda_python_icoads/README_R3.0_Subset.html +154 -0
- rda_python_icoads/__init__.py +1 -0
- rda_python_icoads/imma1_subset.py +764 -0
- rda_python_icoads/rdimma1_csv.f +443 -0
- rda_python_icoads-1.0.1.dist-info/LICENSE +21 -0
- rda_python_icoads-1.0.1.dist-info/METADATA +20 -0
- rda_python_icoads-1.0.1.dist-info/RECORD +11 -0
- rda_python_icoads-1.0.1.dist-info/WHEEL +5 -0
- rda_python_icoads-1.0.1.dist-info/entry_points.txt +2 -0
- rda_python_icoads-1.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1785 @@
|
|
|
1
|
+
#
|
|
2
|
+
###############################################################################
|
|
3
|
+
#
|
|
4
|
+
# Title : PgIMMA.py
|
|
5
|
+
# Author : Zaihua Ji, zji@car.edu
|
|
6
|
+
# Date : 09/13/2020
|
|
7
|
+
# 2025-02-18 transferred to package rda_python_icoads from
|
|
8
|
+
# https://github.com/NCAR/rda-shared-libraries.git
|
|
9
|
+
# Purpose : python library module defining IMMA data
|
|
10
|
+
#
|
|
11
|
+
# Github : https://github.com/NCAR/rda-python-icoads.git
|
|
12
|
+
#
|
|
13
|
+
###############################################################################
|
|
14
|
+
#
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import math
|
|
18
|
+
import numpy
|
|
19
|
+
from rda_python_common import PgLOG
|
|
20
|
+
from rda_python_common import PgUtil
|
|
21
|
+
from rda_python_common import PgDBI
|
|
22
|
+
|
|
23
|
+
# variable definition for each section
|
|
24
|
+
# PREC: 0 - string, 1 - integer, otherwise - float
|
|
25
|
+
# TYPE: 0 - data element
|
|
26
|
+
# 1 - time/location/identity element
|
|
27
|
+
# 2 - IMMA format element
|
|
28
|
+
# 3 - indicator element
|
|
29
|
+
# 4 - QC element(s)
|
|
30
|
+
ICORELOC = { #total length = 45
|
|
31
|
+
# IDX SIZE PREC POS TYPE UNIT Description
|
|
32
|
+
'yr' : [0, 4, 1, 0, 1], #Year
|
|
33
|
+
'mo' : [1, 2, 1, 4, 1], #Month
|
|
34
|
+
'dy' : [2, 2, 1, 6, 1], #Day
|
|
35
|
+
'hr' : [3, 4, 0.01, 8, 1], #Hour
|
|
36
|
+
'lat' : [4, 5, 0.01, 12, 1, "degN"], #Latitude
|
|
37
|
+
'lon' : [5, 6, 0.01, 17, 1, "degE"], #Longitude
|
|
38
|
+
'im' : [6, 2, 1, 23, 2], #IMMA Version
|
|
39
|
+
'attc' : [7, 1, 0, 25, 2], #Attm Count
|
|
40
|
+
'ti' : [8, 1, 1, 26, 3], #Time Indicator
|
|
41
|
+
'li' : [9, 1, 1, 27, 3], #Latitude/Longitude Indicator
|
|
42
|
+
'ds' : [10, 1, 1, 28, 0], #Ship Course
|
|
43
|
+
'vs' : [11, 1, 1, 29, 0], #Ship Speed
|
|
44
|
+
'nid' : [12, 2, 1, 30, 1], #National Source Indicator
|
|
45
|
+
'ii' : [13, 2, 1, 32, 3], #ID Indicator
|
|
46
|
+
'id' : [14, 9, 0, 34, 1], #Identification/Call sign
|
|
47
|
+
'c1' : [15, 2, 0, 43, 1] #Country Code
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ICOREREG = { #total length = 63
|
|
51
|
+
# IDX SIZE PREC POS TYPE UNIT MAX Description
|
|
52
|
+
'di' : [0, 1, 1, 0, 3], #Wind Direction Indicator
|
|
53
|
+
'd' : [1, 3, 1, 1, 0, "deg"], #Wind Direction
|
|
54
|
+
'wi' : [2, 1, 1, 4, 3], #Wind Speed Indicator
|
|
55
|
+
'w' : [3, 3, 0.1, 5, 0, "m/s"], #Wind Speed
|
|
56
|
+
'vi' : [4, 1, 1, 8, 3], #VV Indicator
|
|
57
|
+
'vv' : [5, 2, 1, 9, 0], #Visibility
|
|
58
|
+
'ww' : [6, 2, 1, 11, 0], #Present Weather
|
|
59
|
+
'w1' : [7, 1, 1, 13, 0], #Past Weather
|
|
60
|
+
'slp' : [8, 5, 0.1, 14, 0, "hPa"], #Sea Level Pressure
|
|
61
|
+
'a' : [9, 1, 1, 19, 3], #Characteristic of PPP
|
|
62
|
+
'ppp' : [10, 3, 0.1, 20, 0, "hPa"], #Amount of Pressure Tendency
|
|
63
|
+
'it' : [11, 1, 1, 23, 3], #Temperature Indicator
|
|
64
|
+
'at' : [12, 4, 0.1, 24, 0, "degC"], #Air Temperature
|
|
65
|
+
'wbti' : [13, 1, 1, 28, 3], #WBT Indicator
|
|
66
|
+
'wbt' : [14, 4, 0.1, 29, 0, "degC"], #Wet-Bulb Temperature
|
|
67
|
+
'dpti' : [15, 1, 1, 33, 3], #DPT Indicator
|
|
68
|
+
'dpt' : [16, 4, 0.1, 34, 0, "degC"], #Dew-Point Tmperature
|
|
69
|
+
'si' : [17, 2, 1, 38, 3], #SST Measure Method
|
|
70
|
+
'sst' : [18, 4, 0.1, 40, 0, "degC"], #Sea Surface Temperature
|
|
71
|
+
'n' : [19, 1, 1, 44, 0], #Total Cloud Amount
|
|
72
|
+
'nh' : [20, 1, 1, 45, 0], #Lower Cloud Amount
|
|
73
|
+
'cl' : [21, 1, 0, 46, 3], #Low Cloud Type
|
|
74
|
+
'hi' : [22, 1, 1, 47, 3], #H Indicator
|
|
75
|
+
'h' : [23, 1, 0, 48, 0], #Cloud Height
|
|
76
|
+
'cm' : [24, 1, 0, 49, 3], #Middle Cloud Type
|
|
77
|
+
'ch' : [25, 1, 0, 50, 3], #High Cloud type
|
|
78
|
+
'wd' : [26, 2, 10, 51, 0], #Wave Direction
|
|
79
|
+
'wp' : [27, 2, 1, 53, 0, "s"], #Wave Period
|
|
80
|
+
'wh' : [28, 2, 0.5, 55, 0, "m", 99], #Wave Height
|
|
81
|
+
'sd' : [29, 2, 10, 57, 0], #Swell Direction
|
|
82
|
+
'sp' : [30, 2, 1, 59, 0, "s"], #Swell Period
|
|
83
|
+
'sh' : [31, 2, 0.5, 61, 0, "m", 99] #Swell Height
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
IICOADS = { # atti = ' 1', attl = 65
|
|
87
|
+
# IDX SIZE PREC POS TYPE Description
|
|
88
|
+
'bsi' : [0, 1, 1, 4, 3], #Box System Indicator
|
|
89
|
+
'b10' : [1, 3, 1, 5, 1], #10 Deg Box Number
|
|
90
|
+
'b1' : [2, 2, 1, 8, 1], #1 Deg Box Number
|
|
91
|
+
'dck' : [3, 3, 1, 10, 1], #Deck
|
|
92
|
+
'sid' : [4, 3, 1, 13, 1], #Source ID
|
|
93
|
+
'pt' : [5, 2, 1, 16, 1], #Platform Type
|
|
94
|
+
'dups' : [6, 2, 1, 18, 3], #Dup Status
|
|
95
|
+
'dupc' : [7, 1, 1, 20, 3], #Dup Check
|
|
96
|
+
'tc' : [8, 1, 1, 21, 3], #Track Check
|
|
97
|
+
'pb' : [9, 1, 1, 22, 3], #Pressure Bias
|
|
98
|
+
'wx' : [10, 1, 1, 23, 3], #Wave Period Indicator
|
|
99
|
+
'sx' : [11, 1, 1, 24, 3], #Swell Period Indicator
|
|
100
|
+
'c2' : [12, 2, 1, 25, 1], #2nd Country Code
|
|
101
|
+
'aqcs' : [13, 12, 0, 27, 4], #Adaptive QC Flags
|
|
102
|
+
'nd' : [14, 1, 1, 39, 1], #Night/Day Flag
|
|
103
|
+
'trms' : [15, 6, 0, 40, 4], #Trimming Flags (sf,af,uf,vf,pf,rf)
|
|
104
|
+
'nqcs' : [16, 14, 0, 46, 4], #NCDC-QC Flags (znc,wnc,bnc,xnc,ync,pnc,anc,gnc,dnc,snc,cnc,enc,fnc,tnc)
|
|
105
|
+
'qce' : [17, 2, 1, 60, 4], #External Flag
|
|
106
|
+
'lz' : [18, 1, 1, 62, 4], #Landlocked Flag
|
|
107
|
+
'qcz' : [19, 2, 1, 63, 4] #Source Exclusion Flags
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
IIMMT5 = { # atti = ' 5', attl = 94
|
|
111
|
+
# IDX SIZE PREC POS TYPE UNIT MAX Description
|
|
112
|
+
'os' : [0, 1, 1, 4, 1], #Obervation Source
|
|
113
|
+
'op' : [1, 1, 1, 5, 1], #Observation Platform
|
|
114
|
+
'fm' : [2, 1, 0, 6, 1], #FM Code Version
|
|
115
|
+
'immv' : [3, 1, 0, 7, 1], #IMMT Version
|
|
116
|
+
'ix' : [4, 1, 1, 8, 3], #Station/Weather Indicator
|
|
117
|
+
'w2' : [5, 1, 1, 9, 0], #2nd Past Weather
|
|
118
|
+
'wmi' : [6, 1, 1, 10, 3], #Wave Meassure Indicator
|
|
119
|
+
'sd2' : [7, 2, 10, 11, 0, "deg"], #2nd Swell direction
|
|
120
|
+
'sp2' : [8, 2, 1, 13, 0, "s"], #2nd Swell Period
|
|
121
|
+
'sh2' : [9, 2, 0.5, 15, 0, "m", 99], #2nd Swell Height
|
|
122
|
+
'ics' : [10, 1, 1, 17, 0], #Ice Accretion on Ship
|
|
123
|
+
'es' : [11, 2, 1, 18, 0, "cm"], #Thickness of IS
|
|
124
|
+
'rs' : [12, 1, 1, 20, 0], #Rate of IS
|
|
125
|
+
'ic1' : [13, 1, 0, 21, 0], #Concentration of Sea Ice
|
|
126
|
+
'ic2' : [14, 1, 0, 22, 3], #Stage of Development
|
|
127
|
+
'ic3' : [15, 1, 0, 23, 1], #Ice of Land Origin
|
|
128
|
+
'ic4' : [16, 1, 0, 24, 0], #True Bearing Ice Edge
|
|
129
|
+
'ic5' : [17, 1, 0, 25, 0], #Ice situation/Trend
|
|
130
|
+
'ir' : [18, 1, 1, 26, 3], #Precipitation Data Indicator
|
|
131
|
+
'rrr' : [19, 3, 1, 27, 0, "mm"], #Amount of Precipitation
|
|
132
|
+
'tr' : [20, 1, 1, 30, 3], #Duration of RRR
|
|
133
|
+
'nu' : [21, 1, 0, 31, 1], #National Use
|
|
134
|
+
'qci' : [22, 1, 1, 32, 3], #QC Indicator
|
|
135
|
+
'qis' : [23, 20, 0, 33, 4], #QC Indicator for Fields (qi1-qi20)
|
|
136
|
+
'qi21' : [24, 1, 1, 53, 2], #MQCS Version
|
|
137
|
+
'hdg' : [25, 3, 1, 54, 0, "deg"], #Ship Heading
|
|
138
|
+
'cog' : [26, 3, 1, 57, 0, "deg"], #Cource Over Ground
|
|
139
|
+
'sog' : [27, 2, 1, 60, 0, "kt"], #Speed Over Ground
|
|
140
|
+
'sll' : [28, 2, 1, 62, 0, "m"], #max.ht>Sum Land Ln
|
|
141
|
+
'slhh' : [29, 3, 1, 64, 0, "m"], #Dep. Load Ln.: Sea Lev.
|
|
142
|
+
'rwd' : [30, 3, 1, 67, 0, "deg"], #Relative Wind Direction
|
|
143
|
+
'rws' : [31, 3, 0.1, 70, 0, "m/s"], #Relative wind speed
|
|
144
|
+
'qi22' : [32, 8, 0, 73, 3], #QC Indicator for Fields (qi22-q129)
|
|
145
|
+
'rh' : [33, 4, 0.1, 81, 0, "%"], #Relative Humidity
|
|
146
|
+
'rhi' : [34, 1, 1, 85, 3], #Relative Humidity Indicator
|
|
147
|
+
'awsi' : [35, 1, 1, 86, 3], #AWS Indicator
|
|
148
|
+
'imono' : [36, 7, 1, 87, 1], #IMO Number
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
IMODQC = { # atti = ' 6', attl 68
|
|
152
|
+
# IDX SIZE PREC POS TYPE UNIT Description
|
|
153
|
+
'cccc' : [0, 4, 0, 4, 1], #Collecting Center
|
|
154
|
+
'buid' : [1, 6, 0, 8, 1], #Bulletin ID
|
|
155
|
+
'fbsrc' : [2, 1, 1, 14, 1], #feedback source
|
|
156
|
+
'bmp' : [3, 5, 0.1, 15, 0, "hPa"], #Background SLP
|
|
157
|
+
'bswu' : [4, 4, 0.1, 20, 0, "m/s"], #Background Wind U Comp.
|
|
158
|
+
'swu' : [5, 4, 0.1, 24, 0, "m/s"], #Derived Wind U Comp.
|
|
159
|
+
'bswv' : [6, 4, 0.1, 28, 0, "m/s"], #Background Wind V Comp.
|
|
160
|
+
'swv' : [7, 4, 0.1, 32, 0, "m/s"], #Derived Wind V Comp.
|
|
161
|
+
'bsat' : [8, 4, 0.1, 36, 0, "degC"], #Background Air Temperature
|
|
162
|
+
'bsrh' : [9, 3, 1, 40, 0, "%"], #Background Relative Humidity
|
|
163
|
+
'srh' : [10, 3, 1, 43, 0, "%"], #Derived Relative Humidity
|
|
164
|
+
'bsst' : [11, 5, .01, 46, 0, "degC"], #Background SST
|
|
165
|
+
'mst' : [12, 1, 1, 51, 1], #Model Surface Type
|
|
166
|
+
'msh' : [13, 4, 1, 52, 0, "m"], #Model Height of Land Surface
|
|
167
|
+
'byr' : [14, 4, 1, 56, 1], #Background Year
|
|
168
|
+
'bmo' : [15, 2, 1, 60, 1], #Background Month
|
|
169
|
+
'bdy' : [16, 2, 1, 62, 1], #Background Day
|
|
170
|
+
'bhr' : [17, 2, 1, 64, 1], #Background Hour
|
|
171
|
+
'bfl' : [18, 2, 1, 66, 1, "min"] #Background Forecast Length
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
IMETAVOS = { # atti = ' 7', attl = 58
|
|
175
|
+
# IDX SIZE PREC POS TYPE UNIT Description
|
|
176
|
+
'mds' : [0, 1, 0, 4, 1], #metadata source
|
|
177
|
+
'c1m' : [1, 2, 0, 5, 1], #Recruiting Country
|
|
178
|
+
'opm' : [2, 2, 1, 7, 1], #Type of Ship
|
|
179
|
+
'kov' : [3, 2, 0, 9, 1], #Kind of Vessel
|
|
180
|
+
'cor' : [4, 2, 0, 11, 1], #Country of Registry
|
|
181
|
+
'tob' : [5, 3, 0, 13, 1], #Type of Barometer
|
|
182
|
+
'tot' : [6, 3, 0, 16, 1], #Type of Thermometer
|
|
183
|
+
'eot' : [7, 2, 0, 19, 1], #Exposure of Thermometer
|
|
184
|
+
'lot' : [8, 2, 0, 21, 1], #Screen Location
|
|
185
|
+
'toh' : [9, 1, 0, 23, 1], #Type of Hygrometer
|
|
186
|
+
'eoh' : [10, 2, 0, 24, 1], #Exposure of Hygrometer
|
|
187
|
+
'sim' : [11, 3, 0, 26, 1], #SST Measurement Method
|
|
188
|
+
'lov' : [12, 3, 1, 29, 0, "m"], #Length of Vessel
|
|
189
|
+
'dos' : [13, 2, 1, 32, 0, "m"], #Depth of SST Measusrement
|
|
190
|
+
'hop' : [14, 3, 1, 34, 0, "m"], #Height of Visual Obs. Platform
|
|
191
|
+
'hot' : [15, 3, 1, 37, 0, "m"], #Height of AT Sensor
|
|
192
|
+
'hob' : [16, 3, 1, 40, 0, "m"], #Height of Barometer
|
|
193
|
+
'hoa' : [17, 3, 1, 43, 0, "m"], #Height of Anemometer
|
|
194
|
+
'smf' : [18, 5, 1, 46, 1], #Source Metadata File
|
|
195
|
+
'sme' : [19, 5, 1, 51, 1], #Source Meta. Element
|
|
196
|
+
'smv' : [20, 2, 1, 56, 1] #Source Format Version
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
INOCN = { # atti = ' 8', attl = 102
|
|
200
|
+
# IDX SIZE PREC POS TYPE UNIT Description
|
|
201
|
+
'otv' : [0, 5, 0.001, 4, 0, "degC"], #temperature value
|
|
202
|
+
'otz' : [1, 4, 0.01, 9, 0, "m"], #temperature depth
|
|
203
|
+
'osv' : [2, 5, 0.001, 13, 0], #salinity value
|
|
204
|
+
'osz' : [3, 4, 0.01, 18, 0, "m"], #salinity depth
|
|
205
|
+
'oov' : [4, 4, 0.01, 22, 0, "ml/l"], #dissolved oxygen
|
|
206
|
+
'ooz' : [5, 4, 0.01, 26, 0, "m"], #dissolved oxygen depth
|
|
207
|
+
'opv' : [6, 4, 0.01, 30, 0, "mm/l"], #phosphate value
|
|
208
|
+
'opz' : [7, 4, 0.01, 34, 0, "m"], #phosphate depth
|
|
209
|
+
'osiv' : [8, 5, 0.01, 38, 0, "mm/l"], #silicate value
|
|
210
|
+
'osiz' : [9, 4, 0.01, 43, 0, "m"], #silicate depth
|
|
211
|
+
'onv' : [10, 5, 0.01, 47, 0, "mm/l"], #nitrate value
|
|
212
|
+
'onz' : [11, 4, 0.01, 52, 0, "m"], #nitrate depth
|
|
213
|
+
'ophv' : [12, 3, 0.01, 56, 0], #pH value
|
|
214
|
+
'ophz' : [13, 4, 0.01, 59, 0, "m"], #pH depth
|
|
215
|
+
'ocv' : [14, 4, 0.01, 63, 0, "mg/l"], #total chlorophyll value
|
|
216
|
+
'ocz' : [15, 4, 0.01, 67, 0, "m"], #total chlorophyll depth
|
|
217
|
+
'oav' : [16, 3, 0.01, 71, 0, "me/l"], #alkalinity value
|
|
218
|
+
'oaz' : [17, 4, 0.01, 74, 0, "m"], #alkalinity depth
|
|
219
|
+
'opcv' : [18, 4, 1, 78, 0, "ma"], #partial pressure of carbon dioxide value
|
|
220
|
+
'opcz' : [19, 4, 0.01, 82, 0, "m"], #partial pressure of carbon dioxide depth
|
|
221
|
+
'odv' : [20, 2, 1, 86, 0, "mm/l"], #dissolved inorganic carbon value
|
|
222
|
+
'odz' : [21, 4, 0.01, 88, 0, "m"], #dissolved inorganic carbon depth
|
|
223
|
+
'puid' : [22, 10, 0, 92, 0] #provider's unique record identification
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
IECR = { # atti = ' 9', attl = 32
|
|
227
|
+
# IDX SIZE PREC POS TYPE UNIT Description
|
|
228
|
+
'cce' : [0, 1, 0, 4, 1], #change code
|
|
229
|
+
'wwe' : [1, 2, 1, 5, 0], #present weather
|
|
230
|
+
'ne' : [2, 1, 1, 7, 0], #total cloud amount
|
|
231
|
+
'nhe' : [3, 1, 1, 8, 0], #lower cloud amount
|
|
232
|
+
'he' : [4, 1, 1, 9, 0], #lower cloud base height
|
|
233
|
+
'cle' : [5, 2, 1, 10, 0], #low cloud type
|
|
234
|
+
'cme' : [6, 2, 1, 12, 0], #middle cloud type
|
|
235
|
+
'che' : [7, 1, 1, 14, 0], #high cloud type
|
|
236
|
+
'am' : [8, 3, 0.01, 15, 0, 'okta'], #middle cloud amount
|
|
237
|
+
'ah' : [9, 3, 0.01, 18, 0, 'okta'], #high cloud amount
|
|
238
|
+
'um' : [10, 1, 1, 21, 0, 'okta'], #NOL middle amount
|
|
239
|
+
'uh' : [11, 1, 1, 22, 0, 'okta'], #NOL high amount
|
|
240
|
+
'sbi' : [12, 1, 1, 23, 3], #sky-brightness indicator
|
|
241
|
+
'sa' : [13, 4, 0.01, 24, 1, 'deg'], #solar altitude
|
|
242
|
+
'ri' : [14, 4, 0.01, 28, 1] #relative lunar illuminance
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
IREANQC = { # atti = '95', attl = 61
|
|
246
|
+
# IDX SIZE PREC POS Description
|
|
247
|
+
'icnr' : [0, 2, 1, 4], #input component number
|
|
248
|
+
'fnr' : [1, 2, 1, 6], #field number
|
|
249
|
+
'dpro' : [2, 2, 1, 8], #lead reanalysis data provider
|
|
250
|
+
'dprp' : [3, 2, 1, 10], #reanalysis project short name
|
|
251
|
+
'ufr' : [4, 1, 1, 12], #reanalysis usage flag
|
|
252
|
+
'mfgr' : [5, 7, 1, 13], #model-collocated first guess value
|
|
253
|
+
'mfgsr' : [6, 7, 1, 20], #model-collocated first guess spread
|
|
254
|
+
'mar' : [7, 7, 1, 27], #model-collocated analysis value
|
|
255
|
+
'masr' : [8, 7, 1, 34], #model-collocated analysis spread
|
|
256
|
+
'bcr' : [9, 7, 1, 41], #bias crrected value
|
|
257
|
+
'arcr' : [10, 4, 0, 48], #author reference code
|
|
258
|
+
'cdr' : [11, 8, 1, 52], #creation date
|
|
259
|
+
'asir' : [12, 1, 1, 60], #acess status indcator
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
IIVAD = { # atti = '96', attl = 53
|
|
263
|
+
# IDX SIZE PREC POS Description
|
|
264
|
+
'icni' : [0, 2, 1, 4], #input component number
|
|
265
|
+
'fni' : [1, 2, 1, 6], #field number
|
|
266
|
+
'jvad' : [2, 1, 0, 8], #scaling factor for VAD
|
|
267
|
+
'vad' : [3, 6, 1, 9], #value-added data
|
|
268
|
+
'ivau1' : [4, 1, 0, 15], #type indicator for VAU1
|
|
269
|
+
'jvau1' : [5, 1, 0, 16], #scaleing factor for VAU1
|
|
270
|
+
'vau1' : [6, 6, 1, 17], #uncertainty of type IVAU1
|
|
271
|
+
'ivau2' : [7, 1, 0, 23], #type indicator for VAU2
|
|
272
|
+
'jvau2' : [8, 1, 0, 24], #scaleing factor for VAU2
|
|
273
|
+
'vau2' : [9, 6, 1, 25], #uncertainty of type IVAU2
|
|
274
|
+
'ivau3' : [10, 1, 0, 31], #type indicator for VAU3
|
|
275
|
+
'jvau3' : [11, 1, 0, 32], #scaleing factor for VAU3
|
|
276
|
+
'vau3' : [12, 6, 1, 33], #uncertainty of type IVAU3
|
|
277
|
+
'vqc' : [13, 1, 1, 39], #value-added QC flag
|
|
278
|
+
'arci' : [14, 4, 0, 40], #author reference code-ivad
|
|
279
|
+
#48 'cdi' : [15, 3, 0, 44], #creation day number
|
|
280
|
+
#48 'asii' : [16, 1, 1, 47], #access status indic.
|
|
281
|
+
#53
|
|
282
|
+
'cdi' : [15, 8, 0, 44], #ISO-8601, YYYYMMDD
|
|
283
|
+
#53
|
|
284
|
+
'asii' : [16, 1, 1, 52], #access status indic.
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
#IERROR = { # atti = '97', attl = 0
|
|
288
|
+
# IDX SIZE PREC POS Description
|
|
289
|
+
# 'icn' : [0, 2, 1, 4], #input component number
|
|
290
|
+
# 'fn' : [1, 2, 1, 6], #field number
|
|
291
|
+
# 'cef' : [2, 1, 1, 7], #corrected/errorneous field flag
|
|
292
|
+
# 'errd' : [3, 0, 0, 13], #corrected/errorneous field value
|
|
293
|
+
# 'arce' : [4, 4, 0, 0], #author reference code-eror
|
|
294
|
+
#'ajdne' : [5, 3, 0, 0], #archive adjusted Julian day number-error
|
|
295
|
+
#}
|
|
296
|
+
|
|
297
|
+
IUIDA = { # atti = '98', attl = 15
|
|
298
|
+
# IDX SIZE PREC POS Description
|
|
299
|
+
'uid' : [0, 6, 0, 4], #unique report ID
|
|
300
|
+
'rn1' : [1, 1, 0, 10], #release no.: primary
|
|
301
|
+
'rn2' : [2, 1, 0, 11], #release no.: secondary
|
|
302
|
+
'rn3' : [3, 1, 0, 12], #release no.: tertiary
|
|
303
|
+
'rsa' : [4, 1, 1, 13], #release status indicator
|
|
304
|
+
'irf' : [5, 1, 1, 14] #intermediate reject flag
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
ISUPPL = { # stti = '99' attl = 0 (variable lenth)
|
|
308
|
+
# IDX SIZE PREC POS Description
|
|
309
|
+
'atte' : [0, 1, 1, 4], #Attm Encoding
|
|
310
|
+
'supd' : [1, 0, 0, 5] #Supplemental Data
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
TABLECOUNT = 12
|
|
314
|
+
IMMA_NAMES = ['icoreloc', 'icorereg', 'iicoads', 'iimmt5', 'imodqc', 'imetavos',
|
|
315
|
+
'inocn', 'iecr', 'ireanqc', 'iivad', 'iuida', 'isuppl']
|
|
316
|
+
|
|
317
|
+
#
|
|
318
|
+
# define IMMA sections, core + attms
|
|
319
|
+
#
|
|
320
|
+
IMMAS = {
|
|
321
|
+
# TIDX ATTI ATTL ATTM ATTLS CN
|
|
322
|
+
'icoreloc' : [0, '', 45, ICORELOC, '', 'Core'],
|
|
323
|
+
'icorereg' : [1, '', 63, ICOREREG, '', 'Core'],
|
|
324
|
+
'iicoads' : [2, ' 1', 65, IICOADS, '65', 'Icoads'],
|
|
325
|
+
'iimmt5' : [3, ' 5', 94, IIMMT5, '94', 'Immt'],
|
|
326
|
+
'imodqc' : [4, ' 6', 68, IMODQC, '68', 'Mod-qc'],
|
|
327
|
+
'imetavos' : [5, ' 7', 58, IMETAVOS, '58', 'Meta-vos'],
|
|
328
|
+
'inocn' : [6, ' 8', 102, INOCN, '2U', 'Nocn'],
|
|
329
|
+
'iecr' : [7, ' 9', 32, IECR, '32', 'Ecr'],
|
|
330
|
+
'ireanqc' : [8, '95', 61, IREANQC, '61', 'Rean-qc'],
|
|
331
|
+
#48 'iivad' : [9, '96', 48, IIVAD, '48', 'Ivad'],
|
|
332
|
+
#53
|
|
333
|
+
'iivad' : [9, '96', 53, IIVAD, '53', 'Ivad'],
|
|
334
|
+
'iuida' : [10, '98', 15, IUIDA, '15', 'Uida'],
|
|
335
|
+
'isuppl' : [11, '99', 0, ISUPPL, ' 0', 'Suppl']
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
UIDATTI = '98'
|
|
339
|
+
|
|
340
|
+
MUNIQUE = {
|
|
341
|
+
'ireanqc' : ['arcr', 'cdr'],
|
|
342
|
+
'iivad' : ['arci', 'cdi']
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
MULTI_NAMES = []
|
|
346
|
+
ATTI2NAME = {}
|
|
347
|
+
ATTCPOS = INVENTORY = CURTIDX = CURIIDX = 0
|
|
348
|
+
CURIUID = ''
|
|
349
|
+
IMMA_COUNTS = []
|
|
350
|
+
UIDIDX = 0
|
|
351
|
+
AUTHREFS = {}
|
|
352
|
+
NUM2NAME = {} # cache field names from component/field numbers
|
|
353
|
+
NAME2NUM = {} # cache component/field numbers from field names
|
|
354
|
+
UMATCH = {} # unique matches, use iidx as key
|
|
355
|
+
TINFO = {}
|
|
356
|
+
FIELDINFO = {} # cache the field metadata info for quick find
|
|
357
|
+
ATTM_VARS = {}
|
|
358
|
+
MISSING = -999999
|
|
359
|
+
ERROR = -999999
|
|
360
|
+
LEADUID = 0
|
|
361
|
+
CHKEXIST = 0
|
|
362
|
+
UIDLENGTH = 0 # uid record len
|
|
363
|
+
UIDOFFSET = 0 # uid value offset
|
|
364
|
+
ATTMNAME = None # standalone attm section name to fill
|
|
365
|
+
|
|
366
|
+
#
|
|
367
|
+
# initialize the table information
|
|
368
|
+
#
|
|
369
|
+
def init_table_info():
|
|
370
|
+
|
|
371
|
+
global IMMA_COUNTS, ATTCPOS, UIDATTI
|
|
372
|
+
|
|
373
|
+
IMMA_COUNTS = [0]*TABLECOUNT
|
|
374
|
+
ATTCPOS = ICORELOC['attc'][3]
|
|
375
|
+
UIDATTI = IMMAS['iuida'][1]
|
|
376
|
+
|
|
377
|
+
for aname in IMMA_NAMES:
|
|
378
|
+
imma = IMMAS[aname]
|
|
379
|
+
if imma[1]: ATTI2NAME[imma[1]] = aname
|
|
380
|
+
if aname in MUNIQUE: MULTI_NAMES.append(aname)
|
|
381
|
+
|
|
382
|
+
return 1
|
|
383
|
+
|
|
384
|
+
#
|
|
385
|
+
# identify and return the ATTM name from a given line of standalone attm input
|
|
386
|
+
#
|
|
387
|
+
def identify_attm_name(line):
|
|
388
|
+
|
|
389
|
+
global UIDOFFSET, UIDLENGTH, ATTMNAME
|
|
390
|
+
if LEADUID:
|
|
391
|
+
UIDOFFSET = 0
|
|
392
|
+
UIDLENGTH = 6
|
|
393
|
+
atti = line[6:8]
|
|
394
|
+
elif re.match(r'^9815', line):
|
|
395
|
+
UIDOFFSET = 4
|
|
396
|
+
UIDLENGTH = 15
|
|
397
|
+
atti = line[15:17]
|
|
398
|
+
else:
|
|
399
|
+
atti = None
|
|
400
|
+
|
|
401
|
+
if atti and atti in ATTI2NAME:
|
|
402
|
+
ATTMNAME = ATTI2NAME[atti]
|
|
403
|
+
else:
|
|
404
|
+
ATTMNAME = None
|
|
405
|
+
|
|
406
|
+
return ATTMNAME
|
|
407
|
+
|
|
408
|
+
#
|
|
409
|
+
# cache field information for given attm and variable name
|
|
410
|
+
#
|
|
411
|
+
def cache_field_info(aname, var, uidopt = 0):
|
|
412
|
+
|
|
413
|
+
global FIELDINFO, LEADUID
|
|
414
|
+
if aname not in IMMAS: PgLOG.pglog("{}: Unkown attm name provided to fill field {}".format(aname, var), PgLOG.LGEREX)
|
|
415
|
+
imma = IMMAS[aname]
|
|
416
|
+
attm = imma[3]
|
|
417
|
+
if var not in attm: PgLOG.pglog("{}: Field name not in attm {}".format(var, aname), PgLOG.LGEREX)
|
|
418
|
+
fld = attm[var]
|
|
419
|
+
|
|
420
|
+
if uidopt: LEADUID = uidopt
|
|
421
|
+
FIELDINFO = {'aname' : aname, 'atti' : imma[1], 'attl' : imma[2], 'var' : var, 'fld' : fld, 'prec' : fld[2]}
|
|
422
|
+
|
|
423
|
+
#
|
|
424
|
+
# append the individual fields and return imma records for one line input
|
|
425
|
+
#
|
|
426
|
+
def get_imma_records(line, cdate, records):
|
|
427
|
+
|
|
428
|
+
global CURIIDX, CURIUID
|
|
429
|
+
llen = len(line)
|
|
430
|
+
if llen == 0: return records
|
|
431
|
+
|
|
432
|
+
if CURIUID: # got core section already
|
|
433
|
+
coreidx = 2
|
|
434
|
+
offset = UIDLENGTH
|
|
435
|
+
uid = CURIUID
|
|
436
|
+
else:
|
|
437
|
+
coreidx = 0
|
|
438
|
+
offset = 0
|
|
439
|
+
CURIIDX += 1
|
|
440
|
+
pgrecs = {}
|
|
441
|
+
|
|
442
|
+
while (llen-offset) > 3:
|
|
443
|
+
if coreidx < 2:
|
|
444
|
+
aname = IMMA_NAMES[coreidx]
|
|
445
|
+
coreidx += 1
|
|
446
|
+
else:
|
|
447
|
+
aname = ATTI2NAME[line[offset:offset+2]]
|
|
448
|
+
imma = IMMAS[aname]
|
|
449
|
+
pgrec = get_one_attm(imma[3], offset, line)
|
|
450
|
+
#48 if aname == 'iivad': pgrec['cdi'] = PgUtil.adddate('2014-01-01', 0, 0, I36(pgrec['cdi']), 'YYYMMDD')
|
|
451
|
+
if aname not in records: records[aname] = initialize_attm_records(imma[3])
|
|
452
|
+
if CURIUID:
|
|
453
|
+
append_one_attm(uid, imma[0], imma[3], pgrec, records[aname])
|
|
454
|
+
else:
|
|
455
|
+
pgrecs[aname] = pgrec
|
|
456
|
+
|
|
457
|
+
if not imma[2]: break # force stop for suppl attm
|
|
458
|
+
offset += imma[2]
|
|
459
|
+
|
|
460
|
+
if CURIUID: return records
|
|
461
|
+
|
|
462
|
+
if 'iuida' not in pgrecs: PgLOG.pglog("Miss UID attm: " + line, PgLOG.LGEREX)
|
|
463
|
+
uid = pgrecs['iuida']['uid']
|
|
464
|
+
records['icoreloc']['date'].append(cdate)
|
|
465
|
+
|
|
466
|
+
for aname in pgrecs:
|
|
467
|
+
imma = IMMAS[aname]
|
|
468
|
+
append_one_attm(uid, imma[0], imma[3], pgrecs[aname], records[aname])
|
|
469
|
+
|
|
470
|
+
return records
|
|
471
|
+
|
|
472
|
+
#
|
|
473
|
+
# append the individual fields and return imma records for one line of multi-attm record
|
|
474
|
+
#
|
|
475
|
+
def get_imma_multiple_records(line, records):
|
|
476
|
+
|
|
477
|
+
llen = len(line)
|
|
478
|
+
if llen == 0: return records
|
|
479
|
+
uid = line[4:10]
|
|
480
|
+
offset = 15
|
|
481
|
+
aname = ATTI2NAME[line[15:17]]
|
|
482
|
+
imma = IMMAS[aname]
|
|
483
|
+
if aname not in records: records[aname] = initialize_attm_records(imma[3])
|
|
484
|
+
|
|
485
|
+
while (llen-offset) > 3:
|
|
486
|
+
pgrec = get_one_attm(imma[3], offset, line)
|
|
487
|
+
append_one_attm(uid, imma[0], imma[3], pgrec, records[aname])
|
|
488
|
+
offset += imma[2]
|
|
489
|
+
|
|
490
|
+
return records
|
|
491
|
+
|
|
492
|
+
#
|
|
493
|
+
# read file line and fill a single field value into db
|
|
494
|
+
#
|
|
495
|
+
def set_imma_field(line):
|
|
496
|
+
|
|
497
|
+
llen = len(line)
|
|
498
|
+
var = FIELDINFO['var']
|
|
499
|
+
pgrec = {}
|
|
500
|
+
|
|
501
|
+
if ATTMNAME: # attm name is provided
|
|
502
|
+
coreidx = 2 # skip core sections
|
|
503
|
+
offset = UIDLENGTH
|
|
504
|
+
uid = line[UIDOFFSET:UIDOFFSET+6]
|
|
505
|
+
cont = 1
|
|
506
|
+
else:
|
|
507
|
+
coreidx = 0
|
|
508
|
+
offset = 0
|
|
509
|
+
uid = None
|
|
510
|
+
cont = 2
|
|
511
|
+
|
|
512
|
+
getval = 0
|
|
513
|
+
while (llen-offset) > 3:
|
|
514
|
+
if coreidx < 2:
|
|
515
|
+
aname = IMMA_NAMES[coreidx]
|
|
516
|
+
coreidx += 1
|
|
517
|
+
if aname == FIELDINFO['aname']: getval = 1
|
|
518
|
+
else:
|
|
519
|
+
atti = line[offset:offset+2]
|
|
520
|
+
if atti == UIDATTI:
|
|
521
|
+
uid = line[offset+4:offset+10]
|
|
522
|
+
cont -= 1
|
|
523
|
+
if atti == FIELDINFO['atti']:
|
|
524
|
+
getval = 1
|
|
525
|
+
else:
|
|
526
|
+
aname = ATTI2NAME[atti]
|
|
527
|
+
|
|
528
|
+
if getval:
|
|
529
|
+
fld = FIELDINFO['fld']
|
|
530
|
+
pos = offset + fld[3]
|
|
531
|
+
if fld[1] > 0:
|
|
532
|
+
val = line[pos:pos+fld[1]]
|
|
533
|
+
else:
|
|
534
|
+
val = line[pos:]
|
|
535
|
+
val = val.rstrip(val) # remove trailing whitespaces
|
|
536
|
+
if len(val) > 0:
|
|
537
|
+
if fld[2] > 0:
|
|
538
|
+
cnd = "{} = {}".format(var, val)
|
|
539
|
+
pgrec[var] = int(val)
|
|
540
|
+
else:
|
|
541
|
+
cnd = "{} = '{}'".format(var, val)
|
|
542
|
+
pgrec[var] = val
|
|
543
|
+
getval = 0
|
|
544
|
+
cont -= 1
|
|
545
|
+
offset += FIELDINFO['attl']
|
|
546
|
+
else:
|
|
547
|
+
offset += IMMAS[aname][2]
|
|
548
|
+
|
|
549
|
+
if cont <= 0: break
|
|
550
|
+
|
|
551
|
+
if not pgrec: return 0
|
|
552
|
+
|
|
553
|
+
if not uid: PgLOG.pglog("Miss UID attm: " + line, PgLOG.LGEREX)
|
|
554
|
+
if not get_itidx_date(uid): return 0
|
|
555
|
+
|
|
556
|
+
tname = "{}_{}".format(FIELDINFO['aname'], CURTIDX)
|
|
557
|
+
if PgDBI.pgget(tname, "", "iidx = {} AND {}".format(CURIIDX, cnd)): return 0
|
|
558
|
+
return PgDBI.pgupdt(tname, pgrec, "iidx = {}".format(CURIIDX), PgLOG.LGEREX)
|
|
559
|
+
|
|
560
|
+
#
|
|
561
|
+
# get all field values for a single attm
|
|
562
|
+
#
|
|
563
|
+
def get_one_attm(attm, offset, line):
|
|
564
|
+
|
|
565
|
+
pgrec = {}
|
|
566
|
+
for var in attm:
|
|
567
|
+
fld = attm[var]
|
|
568
|
+
pos = offset + fld[3]
|
|
569
|
+
if fld[1] > 0:
|
|
570
|
+
val = line[pos:pos+fld[1]]
|
|
571
|
+
else:
|
|
572
|
+
val = line[pos:]
|
|
573
|
+
|
|
574
|
+
val = val.rstrip() # remove trailing whitespaces
|
|
575
|
+
if fld[2] > 0:
|
|
576
|
+
pgrec[var] = (int(val) if val else None)
|
|
577
|
+
else:
|
|
578
|
+
pgrec[var] = val
|
|
579
|
+
|
|
580
|
+
return pgrec
|
|
581
|
+
|
|
582
|
+
#
|
|
583
|
+
# Initialize dict records for specified attm table
|
|
584
|
+
#
|
|
585
|
+
def initialize_attm_records(attm):
|
|
586
|
+
|
|
587
|
+
pgrecs = {'iidx' : [], 'uid' : []}
|
|
588
|
+
for var in attm: pgrecs[var] = []
|
|
589
|
+
if 'yr' in attm: pgrecs['date'] = []
|
|
590
|
+
|
|
591
|
+
return pgrecs
|
|
592
|
+
|
|
593
|
+
#
|
|
594
|
+
# append one attm record to the multiple attm records
|
|
595
|
+
#
|
|
596
|
+
def append_one_attm(uid, aidx, attm, pgrec, pgrecs):
|
|
597
|
+
|
|
598
|
+
global IMMA_COUNTS
|
|
599
|
+
pgrecs['iidx'].append(CURIIDX)
|
|
600
|
+
if 'uid' not in attm: pgrecs['uid'].append(uid)
|
|
601
|
+
for var in attm: pgrecs[var].append(pgrec[var])
|
|
602
|
+
IMMA_COUNTS[aidx] += 1 # row index for individual table
|
|
603
|
+
|
|
604
|
+
#
|
|
605
|
+
# get imma attm counts for a given line of imma record
|
|
606
|
+
#
|
|
607
|
+
def get_imma_counts(line, acnts):
|
|
608
|
+
|
|
609
|
+
global CURIIDX, CURIUID
|
|
610
|
+
llen = len(line)
|
|
611
|
+
if llen == 0: return acnts
|
|
612
|
+
|
|
613
|
+
if CURIUID:
|
|
614
|
+
coreidx = 2
|
|
615
|
+
offset = UIDLENGTH
|
|
616
|
+
else:
|
|
617
|
+
coreidx = 0
|
|
618
|
+
offset = 0
|
|
619
|
+
CURIIDX += 1
|
|
620
|
+
|
|
621
|
+
while (llen-offset) > 3:
|
|
622
|
+
if coreidx < 2:
|
|
623
|
+
aname = IMMA_NAMES[coreidx]
|
|
624
|
+
coreidx += 1
|
|
625
|
+
else:
|
|
626
|
+
aname = ATTI2NAME[line[offset:offset+2]]
|
|
627
|
+
imma = IMMAS[aname]
|
|
628
|
+
acnts[imma[0]] += 1
|
|
629
|
+
if not imma[2]: break
|
|
630
|
+
offset += imma[2]
|
|
631
|
+
|
|
632
|
+
return acnts
|
|
633
|
+
|
|
634
|
+
#
|
|
635
|
+
# get imma multiple attm countsfor a given line of multi-attm record
|
|
636
|
+
#
|
|
637
|
+
def get_imma_multiple_counts(line, acnts):
|
|
638
|
+
|
|
639
|
+
llen = len(line)
|
|
640
|
+
if llen == 0: return acnts
|
|
641
|
+
offset = 15
|
|
642
|
+
aname = ATTI2NAME[line[15:17]]
|
|
643
|
+
imma = IMMAS[aname]
|
|
644
|
+
|
|
645
|
+
while (llen-offset) > 3:
|
|
646
|
+
acnts[imma[0]] += 1
|
|
647
|
+
offset += imma[2]
|
|
648
|
+
|
|
649
|
+
return acnts
|
|
650
|
+
|
|
651
|
+
#
|
|
652
|
+
# add multiple imma records into different tables in RDADB
|
|
653
|
+
#
|
|
654
|
+
def add_imma_records(cdate, records):
|
|
655
|
+
|
|
656
|
+
global INVENTORY, CURTIDX
|
|
657
|
+
if INVENTORY and IMMA_NAMES[0] in records: # add counting record into inventory table
|
|
658
|
+
ulen = len(records[IMMA_NAMES[0]]['uid'])
|
|
659
|
+
if ulen > 0: INVENTORY = add_inventory_record(INVENTORY['fname'], cdate, ulen, INVENTORY)
|
|
660
|
+
if CURTIDX < INVENTORY['tidx']: CURTIDX = INVENTORY['tidx']
|
|
661
|
+
tidx = CURTIDX
|
|
662
|
+
else:
|
|
663
|
+
tidx = date2tidx(cdate)
|
|
664
|
+
acnts = [0]*TABLECOUNT
|
|
665
|
+
for i in range(TABLECOUNT):
|
|
666
|
+
if not IMMA_COUNTS[i]: continue
|
|
667
|
+
aname = IMMA_NAMES[i]
|
|
668
|
+
acnts[i] = add_records_to_table(aname, str(tidx), records[aname], cdate)
|
|
669
|
+
IMMA_COUNTS[i] = 0
|
|
670
|
+
|
|
671
|
+
iuida = records['iuida'] if 'iuida' in records else None
|
|
672
|
+
update_control_tables(cdate, acnts, iuida, tidx)
|
|
673
|
+
|
|
674
|
+
return acnts
|
|
675
|
+
|
|
676
|
+
#
|
|
677
|
+
# read attm records for given date
|
|
678
|
+
#
|
|
679
|
+
def read_attm_for_date(aname, cdate, tidx = None):
|
|
680
|
+
|
|
681
|
+
if tidx is None:
|
|
682
|
+
tidx = date2tidx(cdate)
|
|
683
|
+
if tidx is None: return None
|
|
684
|
+
|
|
685
|
+
if aname == IMMA_NAMES[0]: return read_coreloc_for_date(cdate, tidx)
|
|
686
|
+
|
|
687
|
+
table = "{}_{}".format(aname, tidx)
|
|
688
|
+
if not PgDBI.pgcheck(table): return None
|
|
689
|
+
loctable = "{}_{}".format(IMMA_NAMES[0], tidx)
|
|
690
|
+
jtables = "{} a, {} b".format(table, loctable)
|
|
691
|
+
|
|
692
|
+
return PgDBI.pgmget(jtables, "a.*", "b.date = '{}' AND a.iidx = b.iidx ORDER BY iidx".format(cdate), PgLOG.LGEREX)
|
|
693
|
+
|
|
694
|
+
#
|
|
695
|
+
# read core records for given date
|
|
696
|
+
#
|
|
697
|
+
def read_coreloc_for_date(cdate, tidx = None):
|
|
698
|
+
|
|
699
|
+
global CURTIDX
|
|
700
|
+
if not tidx:
|
|
701
|
+
tidx = date2tidx(cdate)
|
|
702
|
+
if not tidx: return None
|
|
703
|
+
CURTIDX = tidx
|
|
704
|
+
|
|
705
|
+
return PgDBI.pgmget("{}_{}".format(IMMA_NAMES[0], tidx), '*', "date = '{}' ORDER BY iidx".format(cdate))
|
|
706
|
+
|
|
707
|
+
#
|
|
708
|
+
# read attm record for given uid
|
|
709
|
+
#
|
|
710
|
+
def read_attm_for_uid(aname, uid, tidx):
|
|
711
|
+
|
|
712
|
+
if aname == IMMA_NAMES[0]: return read_coreloc_for_uid(uid, tidx)
|
|
713
|
+
|
|
714
|
+
table = "{}_{}".format(aname, tidx)
|
|
715
|
+
if not PgDBI.pgcheck(table): return None
|
|
716
|
+
|
|
717
|
+
return PgDBI.pgget(table, "*", "uid = '{}'".format(uid), PgLOG.LGEREX)
|
|
718
|
+
|
|
719
|
+
#
|
|
720
|
+
# read core records for given uid
|
|
721
|
+
#
|
|
722
|
+
def read_coreloc_for_uid(uid, tidx):
|
|
723
|
+
|
|
724
|
+
return PgDBI.pgget("{}_{}".format(IMMA_NAMES[0], tidx), '*', "uid = '{}'".format(uid))
|
|
725
|
+
|
|
726
|
+
#
|
|
727
|
+
# write IMMA records to file
|
|
728
|
+
#
|
|
729
|
+
def write_imma_file(fh, corelocs):
|
|
730
|
+
|
|
731
|
+
(acount, anames, atables, aindices) = get_attm_names(CURTIDX)
|
|
732
|
+
rcnt = len(corelocs['iidx'])
|
|
733
|
+
acnts = [0]*TABLECOUNT
|
|
734
|
+
|
|
735
|
+
for r in range(rcnt):
|
|
736
|
+
coreloc = PgUtil.onerecord(corelocs, r)
|
|
737
|
+
line = get_attm_line(IMMA_NAMES[0], None, 0, coreloc)
|
|
738
|
+
acnts[0] += 1
|
|
739
|
+
ilines = []
|
|
740
|
+
acnt = -1
|
|
741
|
+
for a in range(acount):
|
|
742
|
+
if anames[a] in MUNIQUE:
|
|
743
|
+
(icnt, iline) = get_multiple_attm_line(anames[a], atables[a], coreloc['iidx'])
|
|
744
|
+
if icnt > 0:
|
|
745
|
+
acnt += icnt # un-comment if multiple attms are counted
|
|
746
|
+
acnts[aindices[a]] += icnt
|
|
747
|
+
ilines.append(iline)
|
|
748
|
+
else:
|
|
749
|
+
aline = get_attm_line(anames[a], atables[a], coreloc['iidx'])
|
|
750
|
+
if not aline: continue
|
|
751
|
+
line += aline
|
|
752
|
+
if anames[a] == 'iuida' and ilines:
|
|
753
|
+
for i in len(ilines):
|
|
754
|
+
ilines[i] = aline + ilines[i]
|
|
755
|
+
acnts[aindices[a]] += 1
|
|
756
|
+
acnt += 1
|
|
757
|
+
|
|
758
|
+
if acnt != coreloc['attc']: line[ATTCPOS] = B36(acnt)
|
|
759
|
+
|
|
760
|
+
fh.write(line + "\n") # main record
|
|
761
|
+
if ilines:
|
|
762
|
+
for il in ilines:
|
|
763
|
+
fh.write(il + "\n") # add standalone multiple attm line
|
|
764
|
+
|
|
765
|
+
return acnts
|
|
766
|
+
|
|
767
|
+
#
|
|
768
|
+
# write IMMA records for given date
|
|
769
|
+
#
|
|
770
|
+
def write_imma_records(fh, cdate, tidx, dumpall):
|
|
771
|
+
|
|
772
|
+
acnts = [0]*TABLECOUNT
|
|
773
|
+
if not tidx:
|
|
774
|
+
tidx = date2tidx(cdate)
|
|
775
|
+
if not tidx: return None
|
|
776
|
+
|
|
777
|
+
dcnd = "date = '{}' ORDER BY iidx".format(cdate)
|
|
778
|
+
mtable = "{}_{}".format(IMMA_NAMES[0], tidx)
|
|
779
|
+
pgrecs = PgDBI.pgmget(mtable, "*", dcnd)
|
|
780
|
+
if not pgrecs: return None
|
|
781
|
+
acnts[0] = count = len(pgrecs['iidx'])
|
|
782
|
+
minidx = pgrecs['iidx'][0]
|
|
783
|
+
jcnd = "m.iidx = n.iidx AND " + dcnd
|
|
784
|
+
tcnd = "tidx = {} AND attm =".format(tidx)
|
|
785
|
+
atable = "cntldb.iattm"
|
|
786
|
+
|
|
787
|
+
lines = ['']*count
|
|
788
|
+
attcs = [-2]*count
|
|
789
|
+
if dumpall: ulines = ['']*count
|
|
790
|
+
build_imma_lines(IMMA_NAMES[0], minidx, count, pgrecs, lines, attcs)
|
|
791
|
+
atsave = pgrecs['attc']
|
|
792
|
+
|
|
793
|
+
# dump main record
|
|
794
|
+
for a in range(1, TABLECOUNT):
|
|
795
|
+
aname = IMMA_NAMES[a]
|
|
796
|
+
if aname in MUNIQUE: continue
|
|
797
|
+
if PgDBI.pgget(atable, "", "{} '{}'".format(tcnd, aname)):
|
|
798
|
+
ntable = "{}_{}".format(aname, tidx)
|
|
799
|
+
pgrecs = PgDBI.pgmget("{} m, {} n".format(mtable, ntable), "n.*", jcnd)
|
|
800
|
+
if not pgrecs: continue
|
|
801
|
+
acnts[a] = len(pgrecs['iidx'])
|
|
802
|
+
if dumpall and aname == "iuida":
|
|
803
|
+
build_imma_lines(aname, minidx, acnts[a], pgrecs, ulines, attcs)
|
|
804
|
+
for i in range(count):
|
|
805
|
+
lines[i] += ulines[i]
|
|
806
|
+
else:
|
|
807
|
+
build_imma_lines(aname, minidx, acnts[a], pgrecs, lines, attcs)
|
|
808
|
+
|
|
809
|
+
if dumpall: # append the multi-line attms
|
|
810
|
+
for a in range(1, TABLECOUNT):
|
|
811
|
+
aname = IMMA_NAMES[a]
|
|
812
|
+
if MUNIQUE[aname] is None: continue
|
|
813
|
+
if PgDBI.pgget(atable, "", "{} '{}'".format(tcnd, aname)):
|
|
814
|
+
ntable = "{}_{}".format(aname, tidx)
|
|
815
|
+
pgrecs = PgDBI.pgmget("{} m, {} n".format(mtable, ntable), "n.*", jcnd)
|
|
816
|
+
if not pgrecs: continue
|
|
817
|
+
acnts[a] = len(pgrecs['iidx'])
|
|
818
|
+
append_imma_lines(aname, minidx, acnts[a], pgrecs, ulines, lines, attcs)
|
|
819
|
+
|
|
820
|
+
for i in range(count):
|
|
821
|
+
if attcs[i] != atsave[i]:
|
|
822
|
+
acnt = attcs[i]
|
|
823
|
+
line = lines[i]
|
|
824
|
+
lines[i] = line[0:ATTCPOS] + B36(acnt) + line[ATTCPOS+1:]
|
|
825
|
+
fh.write(lines[i] + "\n")
|
|
826
|
+
|
|
827
|
+
return acnts
|
|
828
|
+
|
|
829
|
+
#
|
|
830
|
+
# build daily imma lines by appending each attm
|
|
831
|
+
#
|
|
832
|
+
def build_imma_lines(aname, minidx, count, pgrecs, lines, attcs):
|
|
833
|
+
|
|
834
|
+
imma = IMMAS[aname]
|
|
835
|
+
attm = imma[3]
|
|
836
|
+
|
|
837
|
+
if aname in ATTM_VARS:
|
|
838
|
+
vars = ATTM_VARS[aname]
|
|
839
|
+
else:
|
|
840
|
+
ATTM_VARS[aname] = vars = order_attm_variables(attm)
|
|
841
|
+
|
|
842
|
+
for i in range(count):
|
|
843
|
+
pgrec = PgUtil.onerecord(pgrecs, i)
|
|
844
|
+
line = ''
|
|
845
|
+
for var in vars:
|
|
846
|
+
vlen = attm[var][1]
|
|
847
|
+
if vlen > 0:
|
|
848
|
+
if pgrec[var] is None:
|
|
849
|
+
line += "{:{}}".format(' ', vlen)
|
|
850
|
+
elif attm[var][2] > 0:
|
|
851
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
852
|
+
else:
|
|
853
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
854
|
+
elif pgrec[var] is not None:
|
|
855
|
+
line += pgrec[var] # append note
|
|
856
|
+
|
|
857
|
+
idx = pgrec['iidx'] - minidx
|
|
858
|
+
lines[idx] += imma[1] + imma[4] + line
|
|
859
|
+
attcs[idx] += 1
|
|
860
|
+
|
|
861
|
+
#
|
|
862
|
+
# append daily imma lines for each multi-line attm
|
|
863
|
+
#
|
|
864
|
+
def append_imma_lines(aname, minidx, count, pgrecs, ulines, lines, attcs):
|
|
865
|
+
|
|
866
|
+
imma = IMMAS[aname]
|
|
867
|
+
attm = imma[3]
|
|
868
|
+
|
|
869
|
+
if ATTM_VARS[aname]:
|
|
870
|
+
vars = ATTM_VARS[aname]
|
|
871
|
+
else:
|
|
872
|
+
ATTM_VARS[aname] = vars = order_attm_variables(attm)
|
|
873
|
+
|
|
874
|
+
pidx = -1
|
|
875
|
+
for i in range(count):
|
|
876
|
+
pgrec = PgUtil.onerecord(pgrecs, i)
|
|
877
|
+
cidx = pgrec['iidx'] - minidx
|
|
878
|
+
if cidx > pidx: lines[cidx] += "\n" + ulines[cidx]
|
|
879
|
+
line = imma[1] + imma[4]
|
|
880
|
+
for var in vars:
|
|
881
|
+
vlen = attm[var][1]
|
|
882
|
+
if pgrec[var] is None:
|
|
883
|
+
line += "{:{}}".format(' ', vlen)
|
|
884
|
+
elif attm[var][2] > 0:
|
|
885
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
886
|
+
else:
|
|
887
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
888
|
+
lines[cidx] += line
|
|
889
|
+
attcs[cidx] += 1
|
|
890
|
+
pidx = cidx
|
|
891
|
+
|
|
892
|
+
#
|
|
893
|
+
# count IMMA records for given date
|
|
894
|
+
#
|
|
895
|
+
def count_imma_records(cdate, tidx, cntall):
|
|
896
|
+
|
|
897
|
+
acnts = [0]*TABLECOUNT
|
|
898
|
+
if not tidx:
|
|
899
|
+
tidx = date2tidx(cdate)
|
|
900
|
+
if not tidx: return None
|
|
901
|
+
|
|
902
|
+
atable = "cntldb.iattm"
|
|
903
|
+
tcnd = "tidx = {}".format(tidx)
|
|
904
|
+
dcnd = "date = '{}'".format(cdate)
|
|
905
|
+
mtable = "{}_{}".format(IMMA_NAMES[0], tidx)
|
|
906
|
+
jcnd = "m.iidx = n.iidx AND " + dcnd
|
|
907
|
+
acnts[0] = PgDBI.pgget(mtable, "", dcnd)
|
|
908
|
+
if not acnts[0]: return None
|
|
909
|
+
|
|
910
|
+
for i in range(1,TABLECOUNT):
|
|
911
|
+
aname = IMMA_NAMES[i]
|
|
912
|
+
if not cntall and aname in MUNIQUE: continue
|
|
913
|
+
if PgDBI.pgget(atable, "", "{} AND attm = '{}'".format(tcnd, aname)):
|
|
914
|
+
ntable = "{}_{}".format(aname, tidx)
|
|
915
|
+
acnts[i] = PgDBI.pgget("{} m, {} n".format(mtable, ntable), "", jcnd)
|
|
916
|
+
|
|
917
|
+
return acnts
|
|
918
|
+
|
|
919
|
+
#
|
|
920
|
+
# add inventory information into control db
|
|
921
|
+
#
|
|
922
|
+
def add_inventory_record(fname, cdate, count, inventory, cntopt = 0):
|
|
923
|
+
|
|
924
|
+
didx = 0
|
|
925
|
+
table = "cntldb.inventory"
|
|
926
|
+
|
|
927
|
+
if cntopt == 2:
|
|
928
|
+
cnd = "date = '{}'".format(cdate)
|
|
929
|
+
pgrec = PgDBI.pgget(table, "didx, count", cnd, PgLOG.LGEREX)
|
|
930
|
+
if not pgrec: PgLOG.pglog("{}: error get record for {}".format(table, cnd), PgLOG.LGEREX)
|
|
931
|
+
count = pgrec['count']
|
|
932
|
+
didx = pgrec['didx']
|
|
933
|
+
record = {}
|
|
934
|
+
else:
|
|
935
|
+
record = {'date' : cdate, 'fname' : fname, 'count' : count}
|
|
936
|
+
|
|
937
|
+
if cntopt != 1:
|
|
938
|
+
record['tidx'] = inventory['tidx']
|
|
939
|
+
record['tcount'] = inventory['tcount'] + count
|
|
940
|
+
record['miniidx'] = inventory['maxiidx'] + 1
|
|
941
|
+
record['maxiidx'] = inventory['maxiidx'] + count
|
|
942
|
+
if record['tcount'] > PgDBI.PGDBI['MAXICNT']:
|
|
943
|
+
record['tidx'] += 1
|
|
944
|
+
record['tcount'] = count
|
|
945
|
+
|
|
946
|
+
if didx:
|
|
947
|
+
cnd = "didx = {}".format(didx)
|
|
948
|
+
if not PgDBI.pgupdt(table, record, cnd, PgLOG.LGEREX):
|
|
949
|
+
PgLOG.pglog("{}: error update table for {}".format(table, cnd), PgLOG.LGEREX)
|
|
950
|
+
else:
|
|
951
|
+
didx = PgDBI.pgadd(table, record, PgLOG.LGEREX|PgLOG.AUTOID)
|
|
952
|
+
|
|
953
|
+
record['didx'] = didx
|
|
954
|
+
if cntopt == 2:
|
|
955
|
+
record['count'] = count
|
|
956
|
+
record['date'] = cdate
|
|
957
|
+
|
|
958
|
+
return record
|
|
959
|
+
|
|
960
|
+
#
|
|
961
|
+
# get the attm names for the current tidx
|
|
962
|
+
#
|
|
963
|
+
def get_attm_names(tidx):
|
|
964
|
+
|
|
965
|
+
anames = []
|
|
966
|
+
atables = []
|
|
967
|
+
aindices = []
|
|
968
|
+
attms = {}
|
|
969
|
+
acnt = 0
|
|
970
|
+
pgrecs = PgDBI.pgmget("cntldb.iattm", "attm", "tidx = {}".format(tidx), PgLOG.LGEREX)
|
|
971
|
+
if not pgrecs: PgLOG.pglog("miss iattm record for tidx = {}".format(tidx), PgLOG.LGEREX)
|
|
972
|
+
for aname in pgrecs['attm']: attms[aname] = 1
|
|
973
|
+
|
|
974
|
+
for i in range(1, TABLECOUNT):
|
|
975
|
+
aname = IMMA_NAMES[i]
|
|
976
|
+
if aname in attms:
|
|
977
|
+
anames.append(aname)
|
|
978
|
+
atables.append("{}_{}".format(aname, tidx))
|
|
979
|
+
aindices.append(i)
|
|
980
|
+
acnt += 1
|
|
981
|
+
|
|
982
|
+
return (acnt, anames, atables, aindices)
|
|
983
|
+
|
|
984
|
+
#
|
|
985
|
+
# get the attm line for the attm name and current iidx
|
|
986
|
+
#
|
|
987
|
+
def get_attm_line(aname, atable, iidx, pgrec):
|
|
988
|
+
|
|
989
|
+
if not pgrec: pgrec = PgDBI.pgget(atable, "*", "iidx = {}".format(iidx), PgLOG.LGEREX)
|
|
990
|
+
return build_one_attm_line(aname, pgrec) if pgrec else None
|
|
991
|
+
|
|
992
|
+
#
|
|
993
|
+
# get the attm line for the multiple attms of current iidx
|
|
994
|
+
#
|
|
995
|
+
def get_multiple_attm_line(aname, atable, iidx):
|
|
996
|
+
|
|
997
|
+
pgrecs = PgDBI.pgmget(atable, "*", "iidx = {} ORDER BY lidx".format(iidx), PgLOG.LGEREX)
|
|
998
|
+
icnt = (len(pgrecs['lidx']) if pgrecs else 0)
|
|
999
|
+
if not icnt: return (0, None)
|
|
1000
|
+
|
|
1001
|
+
iline = ''
|
|
1002
|
+
for i in range(icnt):
|
|
1003
|
+
iline += build_one_attm_line(aname, PgUtil.onerecord(pgrecs, i))
|
|
1004
|
+
|
|
1005
|
+
return (icnt, iline)
|
|
1006
|
+
|
|
1007
|
+
#
|
|
1008
|
+
# build the string line for the attm record and current iidx
|
|
1009
|
+
#
|
|
1010
|
+
def build_one_attm_line(aname, pgrec):
|
|
1011
|
+
|
|
1012
|
+
imma = IMMAS[aname]
|
|
1013
|
+
attm = imma[3]
|
|
1014
|
+
line = imma[1] + imma[4]
|
|
1015
|
+
|
|
1016
|
+
if aname in ATTM_VARS:
|
|
1017
|
+
vars = ATTM_VARS[aname]
|
|
1018
|
+
else:
|
|
1019
|
+
ATTM_VARS[aname] = vars = order_attm_variables(attm)
|
|
1020
|
+
|
|
1021
|
+
for var in vars:
|
|
1022
|
+
vlen = attm[var][1]
|
|
1023
|
+
if vlen > 0:
|
|
1024
|
+
if pgrec[var] is None:
|
|
1025
|
+
line += "{:{}}".format(' ', vlen)
|
|
1026
|
+
elif attm[var][2] > 0:
|
|
1027
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
1028
|
+
else:
|
|
1029
|
+
line += "{:{}}".format(pgrec[var], vlen)
|
|
1030
|
+
elif pgrec[var] is not None:
|
|
1031
|
+
line += pgrec[var] # append note
|
|
1032
|
+
|
|
1033
|
+
return line
|
|
1034
|
+
|
|
1035
|
+
#
|
|
1036
|
+
# find an existing imma record in RDADB
|
|
1037
|
+
#
|
|
1038
|
+
def find_imma_record(coreloc):
|
|
1039
|
+
|
|
1040
|
+
cnd = "date = '{}'".format(coreloc['date'])
|
|
1041
|
+
if coreloc['dy'] is None:
|
|
1042
|
+
cnd += " AND dy IS NULL"
|
|
1043
|
+
if coreloc['hr'] is not None:
|
|
1044
|
+
cnd += " AND hr = {}".format(coreloc['hr'])
|
|
1045
|
+
else:
|
|
1046
|
+
cnd += " AND hr IS NULL"
|
|
1047
|
+
if coreloc['lat'] is not None:
|
|
1048
|
+
cnd += " AND lat = {}".format(coreloc['lat'])
|
|
1049
|
+
else:
|
|
1050
|
+
cnd += " AND lat IS NULL"
|
|
1051
|
+
if coreloc['lon'] is not None:
|
|
1052
|
+
cnd += " AND lon = {}".format(coreloc['lon'])
|
|
1053
|
+
else:
|
|
1054
|
+
cnd += " AND lon IS NULL"
|
|
1055
|
+
if coreloc['id'] is not None:
|
|
1056
|
+
cnd += " AND id = '{}'".format(coreloc['id'])
|
|
1057
|
+
else:
|
|
1058
|
+
cnd += " AND id IS NULL"
|
|
1059
|
+
|
|
1060
|
+
pgrec = PgDBI.pgget("coreloc", "iidx", cnd, PgLOG.LGWNEX)
|
|
1061
|
+
|
|
1062
|
+
return (pgrec['iidx'] if pgrec else 0)
|
|
1063
|
+
|
|
1064
|
+
#
|
|
1065
|
+
# get imma date
|
|
1066
|
+
#
|
|
1067
|
+
def get_imma_date(line):
|
|
1068
|
+
|
|
1069
|
+
if ATTMNAME:
|
|
1070
|
+
return get_itidx_date(line[UIDOFFSET:UIDOFFSET+6])
|
|
1071
|
+
else:
|
|
1072
|
+
return get_record_date(line[0:4], line[4:6], line[6:8])
|
|
1073
|
+
|
|
1074
|
+
#
|
|
1075
|
+
# get the itidx record from given uid
|
|
1076
|
+
#
|
|
1077
|
+
def get_itidx_date(uid):
|
|
1078
|
+
|
|
1079
|
+
global CURIUID, CURIIDX, CURTIDX
|
|
1080
|
+
uidx = uid[0:2].lower()
|
|
1081
|
+
suid = uid[2:6]
|
|
1082
|
+
table = "cntldb.itidx_{}".format(uidx)
|
|
1083
|
+
|
|
1084
|
+
pgrec = PgDBI.pgget(table, "*", "suid = '{}'".format(suid), PgLOG.LGEREX)
|
|
1085
|
+
if not pgrec: return PgLOG.pglog("{}: SKIP suid not in {}".format(suid, table), PgLOG.WARNLG)
|
|
1086
|
+
|
|
1087
|
+
if CHKEXIST: # check
|
|
1088
|
+
table = "{}_{}".format(ATTMNAME, pgrec['tidx'])
|
|
1089
|
+
cnd = "iidx = {}".format(pgrec['iidx'])
|
|
1090
|
+
if ATTMNAME in MUNIQUE:
|
|
1091
|
+
for fname in MUNIQUE[ATTMNAME]:
|
|
1092
|
+
cnd += " AND {} = '{}'".format(fname, pgrec[fname])
|
|
1093
|
+
|
|
1094
|
+
if PgDBI.pgget(table, "", cnd): return None
|
|
1095
|
+
|
|
1096
|
+
CURIUID = uid
|
|
1097
|
+
CURIIDX = pgrec['iidx']
|
|
1098
|
+
CURTIDX = pgrec['tidx']
|
|
1099
|
+
|
|
1100
|
+
return pgrec['date']
|
|
1101
|
+
|
|
1102
|
+
#
|
|
1103
|
+
# get record date for given year, month and day
|
|
1104
|
+
#
|
|
1105
|
+
def get_record_date(yr, mo, dy):
|
|
1106
|
+
|
|
1107
|
+
global CURIUID
|
|
1108
|
+
mo = mo.strip()
|
|
1109
|
+
dy = dy.strip()
|
|
1110
|
+
if not mo: PgLOG.pglog("missing month", PgLOG.LGEREX)
|
|
1111
|
+
|
|
1112
|
+
nyr = int(yr)
|
|
1113
|
+
nmo = int(mo)
|
|
1114
|
+
sym = "{}-{}".format(yr, mo)
|
|
1115
|
+
if dy:
|
|
1116
|
+
ndy = int(dy)
|
|
1117
|
+
if ndy < 1:
|
|
1118
|
+
ndy = 1
|
|
1119
|
+
PgLOG.pglog("{}-{}: set dy {} to 1".format(yr, mo, dy), PgLOG.LOGWRN)
|
|
1120
|
+
else:
|
|
1121
|
+
ndy = 1
|
|
1122
|
+
PgLOG.pglog("{}-{}: set missing dy to 1".format(yr, mo), PgLOG.LOGWRN)
|
|
1123
|
+
|
|
1124
|
+
CURIUID = ''
|
|
1125
|
+
|
|
1126
|
+
cdate = PgUtil.fmtdate(nyr, nmo, ndy)
|
|
1127
|
+
if ndy > 30 or nmo == 2 and ndy > 28:
|
|
1128
|
+
edate = PgUtil.enddate(sym+"-01", 0, 'M')
|
|
1129
|
+
if cdate > edate:
|
|
1130
|
+
cdate = edate
|
|
1131
|
+
PgLOG.pglog("{}: set {}-{} to {}".format(cdate, sym, dy, edate), PgLOG.LOGWRN)
|
|
1132
|
+
|
|
1133
|
+
return cdate
|
|
1134
|
+
|
|
1135
|
+
#
|
|
1136
|
+
# get the tidx from table inventory for given date
|
|
1137
|
+
#
|
|
1138
|
+
def date2tidx(cdate, getend = True):
|
|
1139
|
+
|
|
1140
|
+
table = "cntldb.inventory"
|
|
1141
|
+
pgrec = PgDBI.pgget(table, "tidx", "date = '{}'".format(cdate), PgLOG.LGEREX)
|
|
1142
|
+
if pgrec: return pgrec['tidx']
|
|
1143
|
+
|
|
1144
|
+
if getend:
|
|
1145
|
+
cnd = "date < '{}'".format(cdate)
|
|
1146
|
+
pgrec = PgDBI.pgget(table, "max(tidx) tidx", cnd, PgLOG.LGEREX)
|
|
1147
|
+
else:
|
|
1148
|
+
cnd = "date > '{}'".format(cdate)
|
|
1149
|
+
pgrec = PgDBI.pgget(table, "min(tidx) tidx", cnd, PgLOG.LGEREX)
|
|
1150
|
+
if pgrec:
|
|
1151
|
+
return pgrec['tidx']
|
|
1152
|
+
else:
|
|
1153
|
+
return 1
|
|
1154
|
+
|
|
1155
|
+
#
|
|
1156
|
+
# get the date from table inventory for given iidx
|
|
1157
|
+
#
|
|
1158
|
+
def iidx2date(iidx):
|
|
1159
|
+
|
|
1160
|
+
pgrec = PgDBI.pgget("cntldb.inventory", "date", "miniidx <= {} AND maxiidx >= {}".format(iidx, iidx), PgLOG.LGEREX)
|
|
1161
|
+
return (pgrec['date'] if pgrec else None)
|
|
1162
|
+
|
|
1163
|
+
#
|
|
1164
|
+
# get field name from the component number and field number
|
|
1165
|
+
#
|
|
1166
|
+
def number2name(cn, fn):
|
|
1167
|
+
|
|
1168
|
+
key = cn * 100 + fn
|
|
1169
|
+
if key in NUM2NAME: return NUM2NAME[key]
|
|
1170
|
+
|
|
1171
|
+
if cn > 0:
|
|
1172
|
+
offset = 3
|
|
1173
|
+
for i in range(2, TABLECOUNT):
|
|
1174
|
+
aname = IMMA_NAMES[i]
|
|
1175
|
+
if cn == int(IMMAS[aname][1]): break
|
|
1176
|
+
if i >= TABLECOUNT: PgLOG.pglog("{}: Cannot find Component".format(cn), PgLOG.LGEREX)
|
|
1177
|
+
elif fn < 17:
|
|
1178
|
+
offset = 1
|
|
1179
|
+
aname = IMMA_NAMES[0]
|
|
1180
|
+
else:
|
|
1181
|
+
offset = 17
|
|
1182
|
+
aname = IMMA_NAMES[1]
|
|
1183
|
+
|
|
1184
|
+
attm = IMMAS[aname][3]
|
|
1185
|
+
for fname in attm:
|
|
1186
|
+
if fn == (attm[fname][0]+offset):
|
|
1187
|
+
NUM2NAME[key] = [fname, aname]
|
|
1188
|
+
return NUM2NAME[key]
|
|
1189
|
+
|
|
1190
|
+
PgLOG.pglog("{}: Cannot find field name in Component '{}'".format(fn, aname), PgLOG.LGEREX)
|
|
1191
|
+
|
|
1192
|
+
#
|
|
1193
|
+
# get component number and field number from give field name
|
|
1194
|
+
#
|
|
1195
|
+
def name2number(fname):
|
|
1196
|
+
|
|
1197
|
+
if fname in NAME2NUM: return NAME2NUM[fname]
|
|
1198
|
+
|
|
1199
|
+
for i in range(TABLECOUNT):
|
|
1200
|
+
aname = IMMA_NAMES[i]
|
|
1201
|
+
attm = IMMAS[aname][3]
|
|
1202
|
+
if fname in attm:
|
|
1203
|
+
cn = int(IMMAS[aname][1]) if IMMAS[aname][1] else 0
|
|
1204
|
+
fn = attm[fname][0]
|
|
1205
|
+
if i == 0:
|
|
1206
|
+
fn += 1
|
|
1207
|
+
elif i == 1:
|
|
1208
|
+
fn += 17
|
|
1209
|
+
else:
|
|
1210
|
+
fn += 3
|
|
1211
|
+
|
|
1212
|
+
NAME2NUM[fname] = [cn, fn, aname]
|
|
1213
|
+
return NAME2NUM[fname]
|
|
1214
|
+
|
|
1215
|
+
PgLOG.pglog(fname + ": Cannot find Field Name", PgLOG.LGEREX)
|
|
1216
|
+
|
|
1217
|
+
#
|
|
1218
|
+
# convert integers to floating values
|
|
1219
|
+
#
|
|
1220
|
+
def float_imma_record(record):
|
|
1221
|
+
|
|
1222
|
+
for aname in IMMA_NAMES:
|
|
1223
|
+
if aname not in record: continue
|
|
1224
|
+
attm = IMMAS[aname][3]
|
|
1225
|
+
for key in attm:
|
|
1226
|
+
prec = attm[key][2]
|
|
1227
|
+
if prec == 1 or prec == 0: continue
|
|
1228
|
+
val = record[aname][key] if record[aname][key] else 0
|
|
1229
|
+
if not val: continue
|
|
1230
|
+
record[aname][key] = val * prec
|
|
1231
|
+
|
|
1232
|
+
return record
|
|
1233
|
+
|
|
1234
|
+
#
|
|
1235
|
+
# convert the floating values to integers
|
|
1236
|
+
#
|
|
1237
|
+
def integer_imma_record(record):
|
|
1238
|
+
|
|
1239
|
+
for aname in IMMA_NAMES:
|
|
1240
|
+
if aname not in record: continue
|
|
1241
|
+
attm = IMMAS[aname][3]
|
|
1242
|
+
for key in attm:
|
|
1243
|
+
prec = attm[key][2]
|
|
1244
|
+
if prec == 1 or prec == 0: continue
|
|
1245
|
+
val = record[aname][key] if record[aname][key] else 0
|
|
1246
|
+
if not val: continue
|
|
1247
|
+
if val > 0:
|
|
1248
|
+
record[aname][key] = int(val/prec + 0.5)
|
|
1249
|
+
else:
|
|
1250
|
+
record[aname][key] = int(val/prec - 0.5)
|
|
1251
|
+
|
|
1252
|
+
return record
|
|
1253
|
+
|
|
1254
|
+
#
|
|
1255
|
+
# order attm fields according FN and return ordered field array
|
|
1256
|
+
#
|
|
1257
|
+
def order_attm_variables(attm, aname = None):
|
|
1258
|
+
|
|
1259
|
+
if not attm: attm = IMMAS[aname][3]
|
|
1260
|
+
|
|
1261
|
+
return list(attm)
|
|
1262
|
+
|
|
1263
|
+
#
|
|
1264
|
+
# get max inventory index
|
|
1265
|
+
#
|
|
1266
|
+
def get_inventory_record(didx = 0, cntopt = 0):
|
|
1267
|
+
|
|
1268
|
+
table = "cntldb.inventory"
|
|
1269
|
+
|
|
1270
|
+
if not didx:
|
|
1271
|
+
if cntopt == 2:
|
|
1272
|
+
pgrec = PgDBI.pgget(table, "min(date) mdate", "tcount = 0", PgLOG.LGEREX)
|
|
1273
|
+
if not (pgrec and pgrec['mdate']): PgLOG.pglog(table+": no counted-only inventory record exists", PgLOG.LGEREX)
|
|
1274
|
+
didx = get_inventory_didx(pgrec['mdate'], 1)
|
|
1275
|
+
elif cntopt == 0:
|
|
1276
|
+
pgrec = PgDBI.pgget(table, "max(didx) idx", "", PgLOG.LGEREX)
|
|
1277
|
+
didx = (pgrec['idx'] if pgrec else 0)
|
|
1278
|
+
if didx:
|
|
1279
|
+
cnd = "didx = {}".format(didx)
|
|
1280
|
+
pgrec = PgDBI.pgget(table, "*", cnd, PgLOG.LGEREX)
|
|
1281
|
+
if not pgrec: PgLOG.pglog("{}: error get record for {}".format(table, cnd), PgLOG.LGEREX)
|
|
1282
|
+
else:
|
|
1283
|
+
pgrec = {'date' : '', 'fname' : '', 'miniidx' : 0, 'maxiidx' : 0,
|
|
1284
|
+
'didx' : 0, 'count' : 0, 'tcount' : 0, 'tidx' : 1}
|
|
1285
|
+
|
|
1286
|
+
return pgrec
|
|
1287
|
+
|
|
1288
|
+
#
|
|
1289
|
+
# get previous/later inventory didx for given date
|
|
1290
|
+
#
|
|
1291
|
+
def get_inventory_didx(cdate, prev):
|
|
1292
|
+
|
|
1293
|
+
table = "cntldb.inventory"
|
|
1294
|
+
fld = "didx, date"
|
|
1295
|
+
if prev:
|
|
1296
|
+
cnd = "date < '{}' ORDER BY date DECS".format(cdate)
|
|
1297
|
+
else:
|
|
1298
|
+
cnd = "date > '{}' ORDER BY date ASC".format(cdate)
|
|
1299
|
+
|
|
1300
|
+
pgrec = PgDBI.pgget(table, fld, cnd, PgLOG.LGEREX)
|
|
1301
|
+
if not pgrec: PgLOG.pglog("{}: error get record for {}".format(table, cnd), PgLOG.LGEREX)
|
|
1302
|
+
|
|
1303
|
+
return pgrec['didx']
|
|
1304
|
+
|
|
1305
|
+
#
|
|
1306
|
+
# initialize the global indices
|
|
1307
|
+
#
|
|
1308
|
+
def init_current_indices(leaduid = 0, chkexist = 0):
|
|
1309
|
+
|
|
1310
|
+
global UIDIDX, CURIIDX, CURTIDX, CURIUID, AUTHREFS, LEADUID, CHKEXIST
|
|
1311
|
+
# leading info for iuida
|
|
1312
|
+
UIDIDX = IMMAS['iuida'][0]
|
|
1313
|
+
CURIIDX = 0
|
|
1314
|
+
CURTIDX = 1
|
|
1315
|
+
CURIUID = ''
|
|
1316
|
+
AUTHREFS = {}
|
|
1317
|
+
LEADUID = leaduid
|
|
1318
|
+
CHKEXIST = chkexist
|
|
1319
|
+
|
|
1320
|
+
#
|
|
1321
|
+
# initialize indices for givn date
|
|
1322
|
+
#
|
|
1323
|
+
def init_indices_for_date(cdate, fname):
|
|
1324
|
+
|
|
1325
|
+
global INVENTORY, CURIIDX, CURTIDX
|
|
1326
|
+
if fname:
|
|
1327
|
+
if not INVENTORY: INVENTORY = get_inventory_record()
|
|
1328
|
+
INVENTORY['fname'] = fname
|
|
1329
|
+
CURIIDX = INVENTORY['maxiidx']
|
|
1330
|
+
CURTIDX = INVENTORY['tidx']
|
|
1331
|
+
else:
|
|
1332
|
+
pgrec = PgDBI.pgget("cntldb.inventory", "*", "date = '{}'".format(cdate), PgLOG.LGEREX)
|
|
1333
|
+
if not pgrec: PgLOG.pglog("{}: give date not in inventory yet".format(cdate), PgLOG.LGEREX)
|
|
1334
|
+
if CURIIDX < pgrec['miniidx']:
|
|
1335
|
+
CURIIDX = pgrec['miniidx'] - 1
|
|
1336
|
+
CURTIDX = pgrec['tidx'] - 1
|
|
1337
|
+
|
|
1338
|
+
#
|
|
1339
|
+
# update or add control tables
|
|
1340
|
+
#
|
|
1341
|
+
def update_control_tables(cdate, acnts, iuida, tidx = 0):
|
|
1342
|
+
|
|
1343
|
+
if not tidx: tidx = date2tidx(cdate)
|
|
1344
|
+
|
|
1345
|
+
if iuida and acnts[0]:
|
|
1346
|
+
tname = "cntldb.itidx"
|
|
1347
|
+
records = {}
|
|
1348
|
+
for i in range(acnts[UIDIDX]):
|
|
1349
|
+
auid = iuida['uid'][i][0:2].lower()
|
|
1350
|
+
if auid not in records:
|
|
1351
|
+
records[auid] = {'suid' : [], 'date' : [], 'tidx' : [], 'iidx' : []}
|
|
1352
|
+
records[auid]['suid'].append(iuida['uid'][i][2:6])
|
|
1353
|
+
records[auid]['date'].append(cdate)
|
|
1354
|
+
records[auid]['tidx'].append(tidx)
|
|
1355
|
+
records[auid]['iidx'].append(iuida['iidx'][i])
|
|
1356
|
+
|
|
1357
|
+
for auid in records:
|
|
1358
|
+
add_records_to_table(tname, auid, records[auid], cdate)
|
|
1359
|
+
|
|
1360
|
+
tname = "cntldb.iattm"
|
|
1361
|
+
dname = tname + "_daily"
|
|
1362
|
+
for i in range(TABLECOUNT):
|
|
1363
|
+
if not acnts[i]: continue
|
|
1364
|
+
aname = IMMA_NAMES[i]
|
|
1365
|
+
cnd = "attm = '{}' AND tidx = {}".format(aname, tidx)
|
|
1366
|
+
pgrec = PgDBI.pgget(tname, "aidx, count", cnd, PgLOG.LGWNEX)
|
|
1367
|
+
if pgrec:
|
|
1368
|
+
record = {'count' : (pgrec['count'] + acnts[i])}
|
|
1369
|
+
PgDBI.pgupdt(tname, record, "aidx = {}".format(pgrec['aidx']), PgLOG.LGWNEX)
|
|
1370
|
+
else:
|
|
1371
|
+
record = {'tidx' : tidx, 'attm' : aname, 'count' : acnts[i]}
|
|
1372
|
+
PgDBI.pgadd(tname, record, PgLOG.LGWNEX)
|
|
1373
|
+
|
|
1374
|
+
cnd = "attm = '{}' AND date = '{}'".format(aname, cdate)
|
|
1375
|
+
pgrec = PgDBI.pgget(dname, "aidx, count", cnd, PgLOG.LGWNEX)
|
|
1376
|
+
if pgrec:
|
|
1377
|
+
record = {'count' : (pgrec['count'] + acnts[i])}
|
|
1378
|
+
PgDBI.pgupdt(dname, record, "aidx = {}".format(pgrec['aidx']), PgLOG.LGWNEX)
|
|
1379
|
+
else:
|
|
1380
|
+
record = {'date' : cdate, 'tidx' : tidx, 'attm' : aname, 'count' : acnts[i]}
|
|
1381
|
+
PgDBI.pgadd(dname, record, PgLOG.LGWNEX)
|
|
1382
|
+
|
|
1383
|
+
#
|
|
1384
|
+
# add records to a table
|
|
1385
|
+
#
|
|
1386
|
+
def add_records_to_table(tname, suffix, records, cdate):
|
|
1387
|
+
|
|
1388
|
+
table = "{}_{}".format(tname, suffix)
|
|
1389
|
+
if not PgDBI.pgcheck(table):
|
|
1390
|
+
pgcmd = PgDBI.get_pgddl_command(tname)
|
|
1391
|
+
PgLOG.pgsystem("{} -x {}".format(pgcmd, suffix), PgLOG.LGWNEX)
|
|
1392
|
+
|
|
1393
|
+
cnt = PgDBI.pgmadd(table, records, PgLOG.LGEREX)
|
|
1394
|
+
s = 's' if cnt > 1 else ''
|
|
1395
|
+
PgLOG.pglog("{}: {} records added to {}".format(cdate, cnt, table), PgLOG.LOGWRN)
|
|
1396
|
+
|
|
1397
|
+
return cnt
|
|
1398
|
+
|
|
1399
|
+
#
|
|
1400
|
+
# match imma records for given time/space, and matching variables if provided
|
|
1401
|
+
#
|
|
1402
|
+
# TODO: need more work (avoid of itable)
|
|
1403
|
+
#
|
|
1404
|
+
# return maching count
|
|
1405
|
+
#
|
|
1406
|
+
def match_imma_records(cdate, t1, t2, w, e, s, n, vt):
|
|
1407
|
+
|
|
1408
|
+
if cdate in TINFO:
|
|
1409
|
+
tinfo = TINFO[cdate]
|
|
1410
|
+
if not tinfo: return 0
|
|
1411
|
+
else:
|
|
1412
|
+
tinfo = PgDBI.pgget("cntldb.itable", "*", "bdate <= '{}' AND edate >= '{}'".format(cdate, cdate), PgLOG.LGWNEX)
|
|
1413
|
+
if not tinfo:
|
|
1414
|
+
TINFO[cdate] = 0
|
|
1415
|
+
return 0
|
|
1416
|
+
|
|
1417
|
+
# match time/latitude
|
|
1418
|
+
mrecs = PgDBI.pgmget("icoreloc_{}".format(tinfo['tidx']), "*",
|
|
1419
|
+
"date = '{}' AND hr BETWEEN {} AND {} AND lat BETWEEN {} AND {}".format(cdate, t1, t2, s, n), PgLOG.LGWNEX)
|
|
1420
|
+
if not mrecs: return 0 # no match
|
|
1421
|
+
|
|
1422
|
+
cnt = len(mrecs['iidx'])
|
|
1423
|
+
# match longitude, and/or variables
|
|
1424
|
+
m = 0
|
|
1425
|
+
for i in range(cnt):
|
|
1426
|
+
mrec = PgUtil.onerecord(mrecs, i)
|
|
1427
|
+
if w < e:
|
|
1428
|
+
if mrec['lon'] < w or mrec['lon'] > e: continue
|
|
1429
|
+
else:
|
|
1430
|
+
if mrec['lon'] > e and mrec['lon'] < w: continue
|
|
1431
|
+
|
|
1432
|
+
if not vt or match_imma_vars(tinfo, mrec, vt):
|
|
1433
|
+
iidx = mrec['iidx']
|
|
1434
|
+
if iidx not in UMATCH: UMATCH[iidx] = 1
|
|
1435
|
+
m += 1
|
|
1436
|
+
|
|
1437
|
+
return m
|
|
1438
|
+
|
|
1439
|
+
#
|
|
1440
|
+
# return 1 if a value is found for any given variable 0 otherwise
|
|
1441
|
+
#
|
|
1442
|
+
# TODO: need more work
|
|
1443
|
+
#
|
|
1444
|
+
def match_imma_vars(tinfo, mrec, vt):
|
|
1445
|
+
|
|
1446
|
+
mrecs = {}
|
|
1447
|
+
mrecs['icoreloc'] = mrec
|
|
1448
|
+
iidx = mrec['iidx']
|
|
1449
|
+
tidx = tinfo['tidx']
|
|
1450
|
+
for v in vt:
|
|
1451
|
+
name = vt[v]
|
|
1452
|
+
if name not in mrecs:
|
|
1453
|
+
if name in tinfo:
|
|
1454
|
+
mrecs[name] = PgDBI.pgget("{}_{}".format(name, tidx), "*", "iidx = {}".format(iidx))
|
|
1455
|
+
if not mrecs[name]: mrecs[name] = 0
|
|
1456
|
+
else:
|
|
1457
|
+
mrecs[name] = 0
|
|
1458
|
+
|
|
1459
|
+
if mrecs[name] and mrecs[name][v] is not None: return 1 # found value for variable
|
|
1460
|
+
|
|
1461
|
+
return 0
|
|
1462
|
+
|
|
1463
|
+
#
|
|
1464
|
+
# distant in km to degree in 0.01deg
|
|
1465
|
+
#
|
|
1466
|
+
def distant2degree(la, lo, d, b):
|
|
1467
|
+
|
|
1468
|
+
P = 3.14159265
|
|
1469
|
+
R = 6371
|
|
1470
|
+
|
|
1471
|
+
la *= P/18000
|
|
1472
|
+
lo *= P/18000
|
|
1473
|
+
b *= P/18000
|
|
1474
|
+
|
|
1475
|
+
lat = int(18000 * math.asin(math.sin(la)*math.cos(d/R) + math.cos(la)*math.sin(d/R)*math.cos(b))/P + 0.5)
|
|
1476
|
+
lon = int(18000 * (lo + math.atan2(math.sin(b)*math.sin(d/R)*math.cos(la), math.cos(d/R) - math.sin(la)*math.sin(la)))/P + 0.5)
|
|
1477
|
+
|
|
1478
|
+
return (lat, lon)
|
|
1479
|
+
|
|
1480
|
+
# integer to 36-based string
|
|
1481
|
+
def B36(I36):
|
|
1482
|
+
|
|
1483
|
+
STR36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
1484
|
+
B36 = STR36[I36%36]
|
|
1485
|
+
while I36 >= 36:
|
|
1486
|
+
I36 = int(I36/36)
|
|
1487
|
+
B36 = STR36[I36%36] + B36
|
|
1488
|
+
|
|
1489
|
+
return B36
|
|
1490
|
+
|
|
1491
|
+
# 36-based string to integer
|
|
1492
|
+
def I36(B36):
|
|
1493
|
+
|
|
1494
|
+
STR36 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
1495
|
+
I36 = 0
|
|
1496
|
+
for ch in B36:
|
|
1497
|
+
I36 = I36*36 + int(STR36.find(ch))
|
|
1498
|
+
|
|
1499
|
+
return I36
|
|
1500
|
+
|
|
1501
|
+
#
|
|
1502
|
+
# convert from trimqc2.f:01C by Steve Worley
|
|
1503
|
+
# modified for trinqc2.f01D to trim on RH
|
|
1504
|
+
#
|
|
1505
|
+
def TRIMQC2(record, options = None):
|
|
1506
|
+
|
|
1507
|
+
values = {}
|
|
1508
|
+
values['sst'] = record['icorereg']['sst']
|
|
1509
|
+
values['at'] = record['icorereg']['at']
|
|
1510
|
+
values['d'] = record['icorereg']['d']
|
|
1511
|
+
values['w'] = record['icorereg']['w']
|
|
1512
|
+
values['slp'] = record['icorereg']['slp']
|
|
1513
|
+
values['wbt'] = record['icorereg']['wbt']
|
|
1514
|
+
values['dpt'] = record['icorereg']['dpt']
|
|
1515
|
+
values['rh'] = record['iimmt5']['rh'] if 'iimmt5' in record and record['iimmt5'] else None
|
|
1516
|
+
|
|
1517
|
+
# default to enhenced trimming
|
|
1518
|
+
if not options: options = {'OPDN' : 0, 'OPPT' : 1, 'OPSE' : 0, 'OPCQ' : 0, 'OPTF' : 2, 'OP11' : 1}
|
|
1519
|
+
|
|
1520
|
+
# GET TRIMMING AND OTHER QUALITY CONTROL FLAGS
|
|
1521
|
+
TRFLG = GETTRF(record)
|
|
1522
|
+
flags = TRIMQC0(record['icoreloc']['yr'], record['iicoads']['dck'], record['iicoads']['sid'],
|
|
1523
|
+
record['iicoads']['pt'], record['iicoads']['dups'], TRFLG, options)
|
|
1524
|
+
|
|
1525
|
+
if flags['ZZQF'] == 1: return None # RECORD REJECTED
|
|
1526
|
+
|
|
1527
|
+
if flags['SZQF'] == 1: values['sst'] = None # SST FLAG AND QC APPLICATION
|
|
1528
|
+
if flags['AZQF'] == 1: values['at'] = None # AT FLAG AND QC APPLICATION
|
|
1529
|
+
if flags['WZQF'] == 1: values['d'] = values['w'] = None # WIND, D AND W FLAG AND QC APPLICATION
|
|
1530
|
+
if flags['PZQF'] == 1: values['slp'] = None # SLP FLAG AND QC APPLICATION
|
|
1531
|
+
if flags['RZQF'] == 1: values['rh'] = values['wbt'] = values['dpt'] = None # WBT AND DPT FLAG AND QC APPLICATION
|
|
1532
|
+
|
|
1533
|
+
return values
|
|
1534
|
+
|
|
1535
|
+
#
|
|
1536
|
+
# converted from Fortran code trimqc0.f:01D by Sandy Lubker
|
|
1537
|
+
#
|
|
1538
|
+
def TRIMQC0(YR, DCK, SID, PT, DS, TRFLG, options):
|
|
1539
|
+
|
|
1540
|
+
flags = {'ZZQF' : 1} # INITIAL REPORT REJECTION
|
|
1541
|
+
|
|
1542
|
+
# CHECK IF TRIMMING FLAGS MISSING
|
|
1543
|
+
if TRFLG[0] == 0:
|
|
1544
|
+
PgLOG.pglog('TRIMMING FLAGS MISSING', PgLOG.LGEREX)
|
|
1545
|
+
return flags
|
|
1546
|
+
|
|
1547
|
+
# CHECK RANGES OF OPTIONS
|
|
1548
|
+
if options['OPDN'] < 0 or options['OPDN'] > 2:
|
|
1549
|
+
PgLOG.pglog("OPDN={}".format(options['OPDN']), PgLOG.LGEREX)
|
|
1550
|
+
if options['OPPT'] < 0 or options['OPPT'] > 1:
|
|
1551
|
+
PgLOG.pglog("OPPT={}".format(options['OPPT']), PgLOG.LGEREX)
|
|
1552
|
+
if options['OPSE'] < 0 or options['OPSE'] > 1:
|
|
1553
|
+
PgLOG.pglog("OPSE={}".format(options['OPSE']), PgLOG.LGEREX)
|
|
1554
|
+
if options['OPCQ'] < 0 or options['OPCQ'] > 1:
|
|
1555
|
+
PgLOG.pglog("OPCQ={}".format(options['OPCQ']), PgLOG.LGEREX)
|
|
1556
|
+
if options['OPTF'] < 0 or options['OPTF'] > 3:
|
|
1557
|
+
PgLOG.pglog("OPTF={}".format(options['OPTF']), PgLOG.LGEREX)
|
|
1558
|
+
if options['OP11'] < 0 or options['OP11'] > 1:
|
|
1559
|
+
PgLOG.pglog("OP11={}".format(options['OP11']), PgLOG.LGEREX)
|
|
1560
|
+
|
|
1561
|
+
B2 = TRFLG[0]
|
|
1562
|
+
ND = TRFLG[1]
|
|
1563
|
+
SF = TRFLG[2]
|
|
1564
|
+
AF = TRFLG[3]
|
|
1565
|
+
UF = TRFLG[4]
|
|
1566
|
+
VF = TRFLG[5]
|
|
1567
|
+
PF = TRFLG[6]
|
|
1568
|
+
RF = TRFLG[7]
|
|
1569
|
+
ZQ = TRFLG[8]
|
|
1570
|
+
SQ = TRFLG[9]
|
|
1571
|
+
AQ = TRFLG[10]
|
|
1572
|
+
WQ = TRFLG[11]
|
|
1573
|
+
PQ = TRFLG[12]
|
|
1574
|
+
RQ = TRFLG[13]
|
|
1575
|
+
XQ = TRFLG[14]
|
|
1576
|
+
CQ = TRFLG[15]
|
|
1577
|
+
EQ = TRFLG[16]
|
|
1578
|
+
LZ = TRFLG[17]
|
|
1579
|
+
SZ = TRFLG[18]
|
|
1580
|
+
AZ = TRFLG[19]
|
|
1581
|
+
WZ = TRFLG[20]
|
|
1582
|
+
PZ = TRFLG[21]
|
|
1583
|
+
RZ = TRFLG[22]
|
|
1584
|
+
|
|
1585
|
+
if PT is None: PT = -1
|
|
1586
|
+
if options['OPDN'] == 1:
|
|
1587
|
+
if ND == 2: return flags
|
|
1588
|
+
elif options['OPDN'] == 2:
|
|
1589
|
+
if ND == 1: return flags
|
|
1590
|
+
if DS > 2 and (YR >= 1950 or DS != 6): return flags
|
|
1591
|
+
if LZ == 1: return flags
|
|
1592
|
+
if (ZQ == 1 or ZQ == 3) and YR >= 1950: return flags
|
|
1593
|
+
if YR >= 1980:
|
|
1594
|
+
if SID == 25 and YR > 1984: return flags
|
|
1595
|
+
if SID == 30 and YR > 1984: return flags
|
|
1596
|
+
if SID == 33 and YR < 1986: return flags
|
|
1597
|
+
if options['OPPT'] == 0:
|
|
1598
|
+
if not (PT == 2 or PT == 5 or PT == -1 and DCK == 888): return flags
|
|
1599
|
+
if SID == 70 or SID == 71: return flags
|
|
1600
|
+
elif options['OPPT'] == 0:
|
|
1601
|
+
if PT > 5: return flags
|
|
1602
|
+
if SID == 70 or SID == 71: return flags
|
|
1603
|
+
|
|
1604
|
+
# REMOVE ELEMENT REJECTION
|
|
1605
|
+
flags['ZZQF'] = flags['SZQF'] = flags['AZQF'] = flags['WZQF'] = flags['PZQF'] = flags['RZQF'] = 0
|
|
1606
|
+
|
|
1607
|
+
# SOURCE EXCLUSION FLAGS
|
|
1608
|
+
if YR < 1980 or (PT != 13 and PT != 14 and PT != 16):
|
|
1609
|
+
if options['OPSE'] == 0:
|
|
1610
|
+
if SZ == 1: flags['SZQF'] = 1
|
|
1611
|
+
if AZ == 1: flags['AZQF'] = 1
|
|
1612
|
+
if WZ == 1: flags['WZQF'] = 1
|
|
1613
|
+
if YR >= 1980:
|
|
1614
|
+
if SID == 70 or SID == 71: flags['WZQF'] = 1
|
|
1615
|
+
if PZ == 1: flags['PZQF'] = 1
|
|
1616
|
+
if RZ == 1: flags['RZQF'] = 1
|
|
1617
|
+
|
|
1618
|
+
# COMPOSITE QC FLAGS
|
|
1619
|
+
if options['OPCQ'] == 0:
|
|
1620
|
+
if SQ > 0: flags['SZQF'] = 1
|
|
1621
|
+
if AQ > 0: flags['AZQF'] = 1
|
|
1622
|
+
if WQ > 0: flags['WZQF'] = 1
|
|
1623
|
+
if PQ > 0: flags['PZQF'] = 1
|
|
1624
|
+
if RQ > 0: flags['RZQF'] = 1
|
|
1625
|
+
|
|
1626
|
+
# TRIMMING FLAGS
|
|
1627
|
+
if options['OPTF'] < 3:
|
|
1628
|
+
if SF > (options['OPTF']*2+1):
|
|
1629
|
+
if options['OP11'] == 0 or SF != 11: flags['SZQF'] = 1
|
|
1630
|
+
if AF > (options['OPTF']*2+1): flags['AZQF'] = 1
|
|
1631
|
+
if UF > (options['OPTF']*2+1) or VF > (options['OPTF']*2+1): flags['WZQF'] = 1
|
|
1632
|
+
if PF > (options['OPTF']*2+1):
|
|
1633
|
+
if options['OP11'] == 0 or PF != 11: flags['PZQF'] = 1
|
|
1634
|
+
if RF > (options['OPTF']*2+1): flags['RZQF'] = 1
|
|
1635
|
+
elif options['OPTF'] == 3:
|
|
1636
|
+
if SF > 12: flags['SZQF'] = 1
|
|
1637
|
+
if AF > 12: flags['AZQF'] = 1
|
|
1638
|
+
if UF > 12 or VF > 12: flags['WZQF'] = 1
|
|
1639
|
+
if PF > 12: flags['PZQF'] = 1
|
|
1640
|
+
if RF > 12: flags['RZQF'] = 1
|
|
1641
|
+
|
|
1642
|
+
return flags
|
|
1643
|
+
|
|
1644
|
+
#
|
|
1645
|
+
# get trim flags
|
|
1646
|
+
#
|
|
1647
|
+
def GETTRF(record):
|
|
1648
|
+
|
|
1649
|
+
(ZNC,WNC,BNC,XNC,YNC,PNC,ANC,GNC,DNC,SNC,CNC,ENC,FNC,TNC) = (0,1,2,3,4,5,6,7,8,9,10,11,12,13)
|
|
1650
|
+
(SF,AF,UF,VF,PF,RF) = (0,1,2,3,4,5)
|
|
1651
|
+
|
|
1652
|
+
QCFLG = [None]*14
|
|
1653
|
+
cstr = record['iicoads']['nqcs']
|
|
1654
|
+
if cstr:
|
|
1655
|
+
i = 0
|
|
1656
|
+
for c in cstr:
|
|
1657
|
+
if c != ' ': QCFLG[i] = I36(c)
|
|
1658
|
+
i += 1
|
|
1659
|
+
|
|
1660
|
+
TRIMS = [None]*6
|
|
1661
|
+
cstr = record['iicoads']['trms']
|
|
1662
|
+
if cstr:
|
|
1663
|
+
i = 0
|
|
1664
|
+
for c in cstr:
|
|
1665
|
+
if c != ' ': TRIMS[i] = I36(c)
|
|
1666
|
+
i += 1
|
|
1667
|
+
|
|
1668
|
+
TRFLG = [0]*23
|
|
1669
|
+
if record['icoreloc']['lat'] is not None and record['icoreloc']['lon'] is not None and record['iicoads']['b10'] is not None:
|
|
1670
|
+
TRFLG[0] = B2QXY(QB10(record['iicoads']['b10']),record['icoreloc']['lon'],record['icoreloc']['lat'])
|
|
1671
|
+
if record['iicoads']['nd'] is not None: TRFLG[1] = record['iicoads']['nd']
|
|
1672
|
+
if TRIMS[SF] is not None:
|
|
1673
|
+
TRFLG[2] = TRIMS[SF]
|
|
1674
|
+
TRFLG[3] = TRIMS[AF]
|
|
1675
|
+
TRFLG[4] = TRIMS[UF]
|
|
1676
|
+
TRFLG[5] = TRIMS[VF]
|
|
1677
|
+
TRFLG[6] = TRIMS[PF]
|
|
1678
|
+
TRFLG[7] = TRIMS[RF]
|
|
1679
|
+
|
|
1680
|
+
if QCFLG[ZNC]:
|
|
1681
|
+
if QCFLG[ZNC] >= 7 and QCFLG[ZNC] != 10: TRFLG[8] = 1
|
|
1682
|
+
if QCFLG[SNC] >= 8 and QCFLG[SNC] != 10: TRFLG[9] = 1
|
|
1683
|
+
if QCFLG[ANC] >= 8 and QCFLG[ANC] != 10: TRFLG[10] = 1
|
|
1684
|
+
d = record['icorereg']['d']
|
|
1685
|
+
w = record['icorereg']['w']
|
|
1686
|
+
if d != None and w != None and d >= 1 and d <= 360 and w == 0 and QCFLG[WNC] == 7: TRFLG[11] = 1
|
|
1687
|
+
if QCFLG[PNC] >= 8 and QCFLG[PNC] != 10: TRFLG[12] = 1
|
|
1688
|
+
if QCFLG[GNC] >= 8 and QCFLG[GNC] != 10 or QCFLG[DNC] >= 8 and QCFLG[DNC] != 10: TRFLG[13] = 1
|
|
1689
|
+
if QCFLG[XNC] != 10:
|
|
1690
|
+
if QCFLG[XNC] >= 7:
|
|
1691
|
+
TRFLG[14] = 3
|
|
1692
|
+
elif QCFLG[XNC] >= 4:
|
|
1693
|
+
TRFLG[14] = 2
|
|
1694
|
+
elif QCFLG[XNC] >= 2:
|
|
1695
|
+
TRFLG[14] = 1
|
|
1696
|
+
if QCFLG[CNC] != 10:
|
|
1697
|
+
if QCFLG[CNC] >= 7:
|
|
1698
|
+
TRFLG[15] = 3
|
|
1699
|
+
elif QCFLG[CNC] >= 4:
|
|
1700
|
+
TRFLG[15] = 2
|
|
1701
|
+
elif QCFLG[CNC] >= 2:
|
|
1702
|
+
TRFLG[15] = 1
|
|
1703
|
+
if QCFLG[ENC] != 10:
|
|
1704
|
+
if QCFLG[ENC] >= 7:
|
|
1705
|
+
TRFLG[16] = 3
|
|
1706
|
+
elif QCFLG[ENC] >= 4:
|
|
1707
|
+
TRFLG[16] = 2
|
|
1708
|
+
elif QCFLG[ENC] >= 2:
|
|
1709
|
+
TRFLG[16] = 1
|
|
1710
|
+
|
|
1711
|
+
QC = record['iicoads']['qce']
|
|
1712
|
+
if QC:
|
|
1713
|
+
for i in range(13, 7, -1):
|
|
1714
|
+
TRFLG[i] += 2*(QC%2)
|
|
1715
|
+
QC >>= 1
|
|
1716
|
+
|
|
1717
|
+
if record['iicoads']['lz'] != None: TRFLG[17] = record['iicoads']['lz']
|
|
1718
|
+
|
|
1719
|
+
QC = record['iicoads']['qcz']
|
|
1720
|
+
if QC:
|
|
1721
|
+
for i in range(22, 17, -1):
|
|
1722
|
+
TRFLG[i] += 2*(QC%2)
|
|
1723
|
+
QC >>= 1
|
|
1724
|
+
|
|
1725
|
+
return TRFLG
|
|
1726
|
+
|
|
1727
|
+
#
|
|
1728
|
+
# B10 to Q value
|
|
1729
|
+
#
|
|
1730
|
+
def QB10(B10):
|
|
1731
|
+
|
|
1732
|
+
if B10 < 1 or B10 > 648:
|
|
1733
|
+
return -1
|
|
1734
|
+
else:
|
|
1735
|
+
return 2 + (int((B10-1)/324))*2 - int(((B10-1+3)%36)/18)
|
|
1736
|
+
|
|
1737
|
+
#
|
|
1738
|
+
# Q, X and Y to B2 values
|
|
1739
|
+
#
|
|
1740
|
+
def B2QXY(Q, X, Y):
|
|
1741
|
+
|
|
1742
|
+
YY = (-Y) if Y < 0 else Y
|
|
1743
|
+
|
|
1744
|
+
if Q < 1 or Q > 4 or X < 0 or X > 35999 or YY > 9000: return 0
|
|
1745
|
+
|
|
1746
|
+
if YY < 9000:
|
|
1747
|
+
if (Q%2) == 0:
|
|
1748
|
+
C = int(X/200)
|
|
1749
|
+
if C > 89: C = 89
|
|
1750
|
+
else:
|
|
1751
|
+
C = int(((36000-X)%36000)/200)
|
|
1752
|
+
if C > 89: C = 89
|
|
1753
|
+
C = 179-C
|
|
1754
|
+
|
|
1755
|
+
if int(Q/3) == 0:
|
|
1756
|
+
R = 89-int((9000+Y)/200)
|
|
1757
|
+
else:
|
|
1758
|
+
R = int((9000-Y)/200)
|
|
1759
|
+
|
|
1760
|
+
B2 = 2+R*180+C
|
|
1761
|
+
elif Y == 9000:
|
|
1762
|
+
B2 = 1
|
|
1763
|
+
else:
|
|
1764
|
+
B2 = 16202
|
|
1765
|
+
|
|
1766
|
+
return B2
|
|
1767
|
+
|
|
1768
|
+
#
|
|
1769
|
+
# wind speed/directory to component U and V
|
|
1770
|
+
#
|
|
1771
|
+
def wind2uv(w, d):
|
|
1772
|
+
|
|
1773
|
+
u = v = None
|
|
1774
|
+
|
|
1775
|
+
if w == 0 or d == 361:
|
|
1776
|
+
u = v = 0
|
|
1777
|
+
elif d > 0 and d < 361:
|
|
1778
|
+
d = numpy.deg2rad(d + 180)
|
|
1779
|
+
u = w * math.cos(d)
|
|
1780
|
+
v = w * math.sin(d)
|
|
1781
|
+
|
|
1782
|
+
return (u, v)
|
|
1783
|
+
|
|
1784
|
+
# call to initialize table info when the module is loaded
|
|
1785
|
+
init_table_info()
|