22
33import com .bc .fiduceo .core .Dimension ;
44import com .bc .fiduceo .core .Interval ;
5+ import com .bc .fiduceo .core .NodeType ;
56import com .bc .fiduceo .geometry .Polygon ;
67import com .bc .fiduceo .location .PixelLocator ;
78import com .bc .fiduceo .reader .AcquisitionInfo ;
89import com .bc .fiduceo .reader .Reader ;
910import com .bc .fiduceo .reader .time .TimeLocator ;
10- import com .bc .fiduceo .util . NetCDFUtils ;
11+ import com .bc .fiduceo .reader . time . TimeLocator_MillisSince1970 ;
1112import com .bc .fiduceo .util .VariableProxy ;
13+ import org .esa .snap .core .util .StringUtils ;
1214import ucar .ma2 .Array ;
1315import ucar .ma2 .ArrayInt ;
1416import ucar .ma2 .DataType ;
1517import ucar .ma2 .InvalidRangeException ;
1618import ucar .nc2 .Attribute ;
1719import ucar .nc2 .Variable ;
1820
21+ import java .io .BufferedReader ;
1922import java .io .File ;
23+ import java .io .FileReader ;
2024import java .io .IOException ;
2125import java .util .ArrayList ;
26+ import java .util .Date ;
2227import java .util .List ;
2328
2429import static com .bc .fiduceo .util .NetCDFUtils .*;
@@ -27,19 +32,79 @@ class TaoReader implements Reader {
2732
2833 private final static String REG_EX = "(?:TAO|TRITON)_\\ w+_\\ w+(-\\ w+)??\\ d{4}-\\ d{2}.txt" ;
2934
35+ private ArrayList <TaoRecord > records ;
36+ private TimeLocator timeLocator ;
37+
38+ static TaoRecord parseLine (String line ) {
39+ line = line .replaceAll (" +" , " " ); // ensure that we only have single blanks as separator tb 2023-04-28
40+ final String [] tokens = StringUtils .split (line , new char []{' ' }, true );
41+
42+ final TaoRecord record = new TaoRecord ();
43+ record .time = Integer .parseInt (tokens [0 ]);
44+ record .longitude = Float .parseFloat (tokens [1 ]);
45+ record .latitude = Float .parseFloat (tokens [2 ]);
46+ record .SSS = Float .parseFloat (tokens [3 ]);
47+ record .SST = Float .parseFloat (tokens [4 ]);
48+ record .AIRT = Float .parseFloat (tokens [5 ]);
49+ record .RH = Float .parseFloat (tokens [6 ]);
50+ record .WSPD = Float .parseFloat (tokens [7 ]);
51+ record .WDIR = Float .parseFloat (tokens [8 ]);
52+ record .BARO = Float .parseFloat (tokens [9 ]);
53+ record .RAIN = Float .parseFloat (tokens [10 ]);
54+ record .Q = Integer .parseInt (tokens [11 ]);
55+ record .M = tokens [12 ];
56+
57+ return record ;
58+ }
59+
3060 @ Override
3161 public void open (File file ) throws IOException {
32- throw new RuntimeException ("not implemented" );
62+ try (final FileReader fileReader = new FileReader (file )) {
63+ records = new ArrayList <>();
64+
65+ final BufferedReader bufferedReader = new BufferedReader (fileReader );
66+ String line ;
67+ while ((line = bufferedReader .readLine ()) != null ) {
68+ if (line .startsWith ("#" )) {
69+ // skip comment lines tb 2023-04-28
70+ continue ;
71+ }
72+
73+ final TaoRecord record = parseLine (line );
74+ records .add (record );
75+ }
76+ }
3377 }
3478
3579 @ Override
3680 public void close () throws IOException {
37- throw new RuntimeException ("not implemented" );
81+ if (records != null ) {
82+ records .clear ();
83+ records = null ;
84+ }
85+ timeLocator = null ;
3886 }
3987
4088 @ Override
4189 public AcquisitionInfo read () throws IOException {
42- throw new RuntimeException ("not implemented" );
90+ final AcquisitionInfo acquisitionInfo = new AcquisitionInfo ();
91+ int minTime = Integer .MAX_VALUE ;
92+ int maxTime = Integer .MIN_VALUE ;
93+ for (final TaoRecord record : records ) {
94+ if (record .time < minTime ) {
95+ minTime = record .time ;
96+ }
97+ if (record .time > maxTime ) {
98+ maxTime = record .time ;
99+ }
100+ }
101+
102+ acquisitionInfo .setSensingStart (new Date (minTime * 1000L ));
103+ acquisitionInfo .setSensingStop (new Date (maxTime * 1000L ));
104+
105+ acquisitionInfo .setNodeType (NodeType .UNDEFINED );
106+
107+ return acquisitionInfo ;
43108 }
44109
45110 @ Override
@@ -59,7 +124,10 @@ public PixelLocator getSubScenePixelLocator(Polygon sceneGeometry) throws IOExce
59124
60125 @ Override
61126 public TimeLocator getTimeLocator () throws IOException {
62- throw new RuntimeException ("not implemented" );
127+ if (timeLocator == null ) {
128+ createTimeLocator ();
129+ }
130+ return timeLocator ;
63131 }
64132
65133 @ Override
@@ -91,12 +159,98 @@ public List<Variable> getVariables() throws InvalidRangeException, IOException {
91159 attributes .add (new Attribute (CF_STANDARD_NAME , "longitude" ));
92160 variables .add (new VariableProxy ("longitude" , DataType .FLOAT , attributes ));
93161
162+ attributes = new ArrayList <>();
163+ attributes .add (new Attribute (CF_UNITS_NAME , "degree_north" ));
164+ attributes .add (new Attribute (CF_STANDARD_NAME , "latitude" ));
165+ variables .add (new VariableProxy ("latitude" , DataType .FLOAT , attributes ));
166+
167+ attributes = new ArrayList <>();
168+ attributes .add (new Attribute (CF_UNITS_NAME , "seconds since 1970-01-01" ));
169+ attributes .add (new Attribute (CF_STANDARD_NAME , "time" ));
170+ variables .add (new VariableProxy ("time" , DataType .INT , attributes ));
171+
172+ attributes = new ArrayList <>();
173+ attributes .add (new Attribute (CF_UNITS_NAME , "psu" ));
174+ attributes .add (new Attribute (CF_STANDARD_NAME , "sea_surface_salinity" ));
175+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.999 ));
176+ variables .add (new VariableProxy ("SSS" , DataType .FLOAT , attributes ));
177+
178+ attributes = new ArrayList <>();
179+ attributes .add (new Attribute (CF_UNITS_NAME , "degree_Celsius" ));
180+ attributes .add (new Attribute (CF_STANDARD_NAME , "sea_surface_temperature" ));
181+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.999 ));
182+ variables .add (new VariableProxy ("SST" , DataType .FLOAT , attributes ));
183+
184+ attributes = new ArrayList <>();
185+ attributes .add (new Attribute (CF_UNITS_NAME , "degree_Celsius" ));
186+ attributes .add (new Attribute (CF_STANDARD_NAME , "air_temperature" ));
187+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.99 ));
188+ variables .add (new VariableProxy ("AIRT" , DataType .FLOAT , attributes ));
189+
190+ attributes = new ArrayList <>();
191+ attributes .add (new Attribute (CF_UNITS_NAME , "percent" ));
192+ attributes .add (new Attribute (CF_STANDARD_NAME , "relative_humidity" ));
193+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.99 ));
194+ variables .add (new VariableProxy ("RH" , DataType .FLOAT , attributes ));
195+
196+ attributes = new ArrayList <>();
197+ attributes .add (new Attribute (CF_UNITS_NAME , "m/s" ));
198+ attributes .add (new Attribute (CF_STANDARD_NAME , "wind_speed" ));
199+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -99.9 ));
200+ variables .add (new VariableProxy ("WSPD" , DataType .FLOAT , attributes ));
201+
202+ attributes = new ArrayList <>();
203+ attributes .add (new Attribute (CF_UNITS_NAME , "degree" ));
204+ attributes .add (new Attribute (CF_STANDARD_NAME , "wind_to_direction" ));
205+ attributes .add (new Attribute (CF_LONG_NAME , "Wind To Direction degree true in Oceanographic Convention" ));
206+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -99.9 ));
207+ variables .add (new VariableProxy ("WDIR" , DataType .FLOAT , attributes ));
208+
209+ attributes = new ArrayList <>();
210+ attributes .add (new Attribute (CF_UNITS_NAME , "hPa" ));
211+ attributes .add (new Attribute (CF_STANDARD_NAME , "air_pressure_at_mean_sea_level" ));
212+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.9 ));
213+ variables .add (new VariableProxy ("BARO" , DataType .FLOAT , attributes ));
214+
215+ attributes = new ArrayList <>();
216+ attributes .add (new Attribute (CF_UNITS_NAME , "mm/hour" ));
217+ attributes .add (new Attribute (CF_STANDARD_NAME , "rainfall_rate" ));
218+ attributes .add (new Attribute (CF_FILL_VALUE_NAME , -9.99 ));
219+ variables .add (new VariableProxy ("RAIN" , DataType .FLOAT , attributes ));
220+
221+ attributes = new ArrayList <>();
222+ attributes .add (new Attribute (CF_LONG_NAME , "Data Quality Codes" ));
223+ variables .add (new VariableProxy ("Q" , DataType .INT , attributes ));
224+ /*
225+ @todo 1 tb/tb move this to documentation 2023-04-28
226+ Data Quality Codes(Q):
227+ 0 = unknown
228+ 1 = good data
229+ 2 = probably good data
230+ 3 = questionable data
231+ 4 = bad data
232+ 5 = adjusted data
233+ 9 = missing data
234+ */
235+ attributes = new ArrayList <>();
236+ attributes .add (new Attribute (CF_LONG_NAME , "Data Mode Codes" ));
237+ variables .add (new VariableProxy ("M" , DataType .STRING , attributes ));
238+
239+ /*
240+ @todo 1 tb/tb move this to documentation 2023-04-28
241+ Data Mode Codes(M):
242+ R = real-time data
243+ P = provisional data
244+ D = delayed mode data
245+ M = mixed real-time and delayed mode data
246+ */
247+
94248 return variables ;
95249 }
96250
97251 @ Override
98252 public Dimension getProductSize () throws IOException {
99- throw new RuntimeException ( "not implemented" );
253+ return new Dimension ( "product_size" , 1 , records . size () );
100254 }
101255
102256 @ Override
@@ -108,4 +262,16 @@ public String getLongitudeVariableName() {
108262 public String getLatitudeVariableName () {
109263 return "latitude" ;
110264 }
265+
266+ private void createTimeLocator () {
267+ long [] timeArray = new long [records .size ()];
268+
269+ int i = 0 ;
270+ for (final TaoRecord record : records ) {
271+ timeArray [i ] = record .time * 1000L ;
272+ i ++;
273+ }
274+
275+ timeLocator = new TimeLocator_MillisSince1970 (timeArray );
276+ }
111277}
0 commit comments