Skip to content

Commit 0f856b5

Browse files
authored
Merge pull request #13 from bcdev/TAO_TB
Tao tb
2 parents a306ce4 + 00601c4 commit 0f856b5

34 files changed

+2100
-2
lines changed

core/src/main/java/com/bc/fiduceo/reader/insitu/ndbc/NdbcSMReader.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ public TimeLocator getTimeLocator() throws IOException {
116116
@Override
117117
public Array readRaw(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
118118
final SmRecord record = records.get(centerY);
119+
// @todo 2 tb/tb handle positions out of range 2023-05-02
119120

120121
switch (variableName) {
121122
case STATION_ID:
Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
package com.bc.fiduceo.reader.insitu.tao;
2+
3+
import com.bc.fiduceo.core.Dimension;
4+
import com.bc.fiduceo.core.Interval;
5+
import com.bc.fiduceo.core.NodeType;
6+
import com.bc.fiduceo.geometry.Polygon;
7+
import com.bc.fiduceo.location.PixelLocator;
8+
import com.bc.fiduceo.reader.AcquisitionInfo;
9+
import com.bc.fiduceo.reader.Reader;
10+
import com.bc.fiduceo.reader.netcdf.StringVariable;
11+
import com.bc.fiduceo.reader.time.TimeLocator;
12+
import com.bc.fiduceo.reader.time.TimeLocator_MillisSince1970;
13+
import com.bc.fiduceo.util.NetCDFUtils;
14+
import com.bc.fiduceo.util.VariableProxy;
15+
import org.esa.snap.core.util.StringUtils;
16+
import ucar.ma2.Array;
17+
import ucar.ma2.ArrayInt;
18+
import ucar.ma2.DataType;
19+
import ucar.ma2.InvalidRangeException;
20+
import ucar.nc2.Attribute;
21+
import ucar.nc2.Variable;
22+
23+
import java.io.BufferedReader;
24+
import java.io.File;
25+
import java.io.FileReader;
26+
import java.io.IOException;
27+
import java.util.ArrayList;
28+
import java.util.Date;
29+
import java.util.List;
30+
31+
import static com.bc.fiduceo.util.NetCDFUtils.*;
32+
33+
class TaoReader implements Reader {
34+
35+
private final static String REG_EX = "(?:TAO|TRITON)_\\w+_\\w+(-\\w+)??\\d{4}-\\d{2}.txt";
36+
private static final String TIME = "time";
37+
private static final String LONGITUDE = "longitude";
38+
private static final String LATITUDE = "latitude";
39+
private static final String SSS = "SSS";
40+
private static final float SSS_FILL = -9.999f;
41+
private static final String SST = "SST";
42+
private static final float SST_FILL = -9.999f;
43+
private static final String AIRT = "AIRT";
44+
private static final double AIRT_FILL = -9.99;
45+
private static final String RH = "RH";
46+
private static final double RH_FILL = -9.99;
47+
private static final String WSPD = "WSPD";
48+
private static final double WSPD_FILL = -99.9;
49+
private static final String WDIR = "WDIR";
50+
private static final double WDIR_FILL = -99.9;
51+
private static final String BARO = "BARO";
52+
private static final double BARO_FILL = -9.9;
53+
private static final String RAIN = "RAIN";
54+
private static final double RAIN_FILL = -9.99;
55+
private static final String Q = "Q";
56+
private static final String M = "M";
57+
58+
private ArrayList<TaoRecord> records;
59+
private TimeLocator timeLocator;
60+
61+
static TaoRecord parseLine(String line) {
62+
line = line.replaceAll(" +", " "); // ensure that we only have single blanks as separator tb 2023-04-28
63+
final String[] tokens = StringUtils.split(line, new char[]{' '}, true);
64+
65+
final TaoRecord record = new TaoRecord();
66+
record.time = Integer.parseInt(tokens[0]);
67+
record.longitude = Float.parseFloat(tokens[1]);
68+
record.latitude = Float.parseFloat(tokens[2]);
69+
record.SSS = Float.parseFloat(tokens[3]);
70+
record.SST = Float.parseFloat(tokens[4]);
71+
record.AIRT = Float.parseFloat(tokens[5]);
72+
record.RH = Float.parseFloat(tokens[6]);
73+
record.WSPD = Float.parseFloat(tokens[7]);
74+
record.WDIR = Float.parseFloat(tokens[8]);
75+
record.BARO = Float.parseFloat(tokens[9]);
76+
record.RAIN = Float.parseFloat(tokens[10]);
77+
record.Q = Integer.parseInt(tokens[11]);
78+
record.M = tokens[12];
79+
80+
return record;
81+
}
82+
83+
@Override
84+
public void open(File file) throws IOException {
85+
try (final FileReader fileReader = new FileReader(file)) {
86+
records = new ArrayList<>();
87+
88+
final BufferedReader bufferedReader = new BufferedReader(fileReader);
89+
String line;
90+
while ((line = bufferedReader.readLine()) != null) {
91+
if (line.startsWith("#")) {
92+
// skip comment lines tb 2023-04-28
93+
continue;
94+
}
95+
96+
final TaoRecord record = parseLine(line);
97+
records.add(record);
98+
}
99+
}
100+
}
101+
102+
@Override
103+
public void close() throws IOException {
104+
if (records != null) {
105+
records.clear();
106+
records = null;
107+
}
108+
timeLocator = null;
109+
}
110+
111+
@Override
112+
public AcquisitionInfo read() throws IOException {
113+
final AcquisitionInfo acquisitionInfo = new AcquisitionInfo();
114+
int minTime = Integer.MAX_VALUE;
115+
int maxTime = Integer.MIN_VALUE;
116+
for (final TaoRecord record : records) {
117+
if (record.time < minTime) {
118+
minTime = record.time;
119+
}
120+
if (record.time > maxTime) {
121+
maxTime = record.time;
122+
}
123+
}
124+
125+
acquisitionInfo.setSensingStart(new Date(minTime * 1000L));
126+
acquisitionInfo.setSensingStop(new Date(maxTime * 1000L));
127+
128+
acquisitionInfo.setNodeType(NodeType.UNDEFINED);
129+
130+
return acquisitionInfo;
131+
}
132+
133+
@Override
134+
public String getRegEx() {
135+
return REG_EX;
136+
}
137+
138+
@Override
139+
public PixelLocator getPixelLocator() throws IOException {
140+
throw new RuntimeException("not implemented");
141+
}
142+
143+
@Override
144+
public PixelLocator getSubScenePixelLocator(Polygon sceneGeometry) throws IOException {
145+
throw new RuntimeException("not implemented");
146+
}
147+
148+
@Override
149+
public TimeLocator getTimeLocator() throws IOException {
150+
if (timeLocator == null) {
151+
createTimeLocator();
152+
}
153+
return timeLocator;
154+
}
155+
156+
@Override
157+
public int[] extractYearMonthDayFromFilename(String fileName) {
158+
final int endIdx = fileName.indexOf(".txt");
159+
final String yearString = fileName.substring(endIdx - 7, endIdx - 3);
160+
final String monthString = fileName.substring(endIdx - 2, endIdx);
161+
162+
final int[] ymd = new int[3];
163+
ymd[0] = Integer.parseInt(yearString);
164+
ymd[1] = Integer.parseInt(monthString);
165+
ymd[2] = 1;
166+
return ymd;
167+
}
168+
169+
@Override
170+
public Array readRaw(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
171+
final TaoRecord record = records.get(centerY);
172+
173+
switch (variableName) {
174+
case TIME:
175+
return createResultArray(record.time, NetCDFUtils.getDefaultFillValue(DataType.INT, false), DataType.INT, interval);
176+
177+
case LONGITUDE:
178+
return createResultArray(record.longitude, NetCDFUtils.getDefaultFillValue(DataType.FLOAT, false), DataType.FLOAT, interval);
179+
180+
case LATITUDE:
181+
return createResultArray(record.latitude, NetCDFUtils.getDefaultFillValue(DataType.FLOAT, false), DataType.FLOAT, interval);
182+
183+
case SSS:
184+
return createResultArray(record.SSS, SSS_FILL, DataType.FLOAT, interval);
185+
186+
case SST:
187+
return createResultArray(record.SST, SST_FILL, DataType.FLOAT, interval);
188+
189+
case AIRT:
190+
return createResultArray(record.AIRT, AIRT_FILL, DataType.FLOAT, interval);
191+
192+
case RH:
193+
return createResultArray(record.RH, RH_FILL, DataType.FLOAT, interval);
194+
195+
case WSPD:
196+
return createResultArray(record.WSPD, WSPD_FILL, DataType.FLOAT, interval);
197+
198+
case WDIR:
199+
return createResultArray(record.WDIR, WDIR_FILL, DataType.FLOAT, interval);
200+
201+
case BARO:
202+
return createResultArray(record.BARO, BARO_FILL, DataType.FLOAT, interval);
203+
204+
case RAIN:
205+
return createResultArray(record.RAIN, RAIN_FILL, DataType.FLOAT, interval);
206+
207+
case Q:
208+
return createResultArray(record.Q, NetCDFUtils.getDefaultFillValue(DataType.INT, false), DataType.INT, interval);
209+
210+
case M:
211+
final Array resultArray = Array.factory(DataType.STRING, new int[]{1, 1});
212+
resultArray.setObject(0, record.M);
213+
return resultArray;
214+
}
215+
216+
return null;
217+
}
218+
219+
@Override
220+
public Array readScaled(int centerX, int centerY, Interval interval, String variableName) throws IOException, InvalidRangeException {
221+
return readRaw(centerX, centerY, interval, variableName); // no scaled data in this product type tb 2023-05-02
222+
}
223+
224+
@Override
225+
public ArrayInt.D2 readAcquisitionTime(int x, int y, Interval interval) throws IOException, InvalidRangeException {
226+
final Array timeArray = readRaw(x, y, interval, TIME);
227+
228+
return (ArrayInt.D2) timeArray;
229+
}
230+
231+
@Override
232+
public List<Variable> getVariables() throws InvalidRangeException, IOException {
233+
final ArrayList<Variable> variables = new ArrayList<>();
234+
235+
List<Attribute> attributes = new ArrayList<>();
236+
attributes.add(new Attribute(CF_UNITS_NAME, "degree_east"));
237+
attributes.add(new Attribute(CF_STANDARD_NAME, "longitude"));
238+
variables.add(new VariableProxy(LONGITUDE, DataType.FLOAT, attributes));
239+
240+
attributes = new ArrayList<>();
241+
attributes.add(new Attribute(CF_UNITS_NAME, "degree_north"));
242+
attributes.add(new Attribute(CF_STANDARD_NAME, "latitude"));
243+
variables.add(new VariableProxy(LATITUDE, DataType.FLOAT, attributes));
244+
245+
attributes = new ArrayList<>();
246+
attributes.add(new Attribute(CF_UNITS_NAME, "seconds since 1970-01-01"));
247+
attributes.add(new Attribute(CF_STANDARD_NAME, "time"));
248+
variables.add(new VariableProxy(TIME, DataType.INT, attributes));
249+
250+
attributes = new ArrayList<>();
251+
attributes.add(new Attribute(CF_UNITS_NAME, "psu"));
252+
attributes.add(new Attribute(CF_STANDARD_NAME, "sea_surface_salinity"));
253+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, SSS_FILL));
254+
variables.add(new VariableProxy(SSS, DataType.FLOAT, attributes));
255+
256+
attributes = new ArrayList<>();
257+
attributes.add(new Attribute(CF_UNITS_NAME, "degree_Celsius"));
258+
attributes.add(new Attribute(CF_STANDARD_NAME, "sea_surface_temperature"));
259+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, SST_FILL));
260+
variables.add(new VariableProxy(SST, DataType.FLOAT, attributes));
261+
262+
attributes = new ArrayList<>();
263+
attributes.add(new Attribute(CF_UNITS_NAME, "degree_Celsius"));
264+
attributes.add(new Attribute(CF_STANDARD_NAME, "air_temperature"));
265+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, AIRT_FILL));
266+
variables.add(new VariableProxy(AIRT, DataType.FLOAT, attributes));
267+
268+
attributes = new ArrayList<>();
269+
attributes.add(new Attribute(CF_UNITS_NAME, "percent"));
270+
attributes.add(new Attribute(CF_STANDARD_NAME, "relative_humidity"));
271+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, RH_FILL));
272+
variables.add(new VariableProxy(RH, DataType.FLOAT, attributes));
273+
274+
attributes = new ArrayList<>();
275+
attributes.add(new Attribute(CF_UNITS_NAME, "m/s"));
276+
attributes.add(new Attribute(CF_STANDARD_NAME, "wind_speed"));
277+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, WSPD_FILL));
278+
variables.add(new VariableProxy(WSPD, DataType.FLOAT, attributes));
279+
280+
attributes = new ArrayList<>();
281+
attributes.add(new Attribute(CF_UNITS_NAME, "degree"));
282+
attributes.add(new Attribute(CF_STANDARD_NAME, "wind_to_direction"));
283+
attributes.add(new Attribute(CF_LONG_NAME, "Wind To Direction degree true in Oceanographic Convention"));
284+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, WDIR_FILL));
285+
variables.add(new VariableProxy(WDIR, DataType.FLOAT, attributes));
286+
287+
attributes = new ArrayList<>();
288+
attributes.add(new Attribute(CF_UNITS_NAME, "hPa"));
289+
attributes.add(new Attribute(CF_STANDARD_NAME, "air_pressure_at_mean_sea_level"));
290+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, BARO_FILL));
291+
variables.add(new VariableProxy(BARO, DataType.FLOAT, attributes));
292+
293+
attributes = new ArrayList<>();
294+
attributes.add(new Attribute(CF_UNITS_NAME, "mm/hour"));
295+
attributes.add(new Attribute(CF_STANDARD_NAME, "rainfall_rate"));
296+
attributes.add(new Attribute(CF_FILL_VALUE_NAME, RAIN_FILL));
297+
variables.add(new VariableProxy(RAIN, DataType.FLOAT, attributes));
298+
299+
attributes = new ArrayList<>();
300+
attributes.add(new Attribute(CF_LONG_NAME, "Data Quality Codes"));
301+
variables.add(new VariableProxy(Q, DataType.INT, attributes));
302+
/*
303+
@todo 1 tb/tb move this to documentation 2023-04-28
304+
Data Quality Codes(Q):
305+
0 = unknown
306+
1 = good data
307+
2 = probably good data
308+
3 = questionable data
309+
4 = bad data
310+
5 = adjusted data
311+
9 = missing data
312+
*/
313+
attributes = new ArrayList<>();
314+
attributes.add(new Attribute(CF_LONG_NAME, "Data Mode Codes"));
315+
variables.add(new StringVariable(new VariableProxy(M, DataType.STRING, attributes), 8));
316+
317+
/*
318+
@todo 1 tb/tb move this to documentation 2023-04-28
319+
Data Mode Codes(M):
320+
R = real-time data
321+
P = provisional data
322+
D = delayed mode data
323+
M = mixed real-time and delayed mode data
324+
*/
325+
326+
return variables;
327+
}
328+
329+
@Override
330+
public Dimension getProductSize() throws IOException {
331+
return new Dimension("product_size", 1, records.size());
332+
}
333+
334+
@Override
335+
public String getLongitudeVariableName() {
336+
return "longitude";
337+
}
338+
339+
@Override
340+
public String getLatitudeVariableName() {
341+
return "latitude";
342+
}
343+
344+
private void createTimeLocator() {
345+
long[] timeArray = new long[records.size()];
346+
347+
int i = 0;
348+
for (final TaoRecord record : records) {
349+
timeArray[i] = record.time * 1000L;
350+
i++;
351+
}
352+
353+
timeLocator = new TimeLocator_MillisSince1970(timeArray);
354+
}
355+
356+
// @todo 2 tb/tb create generic in-itu record and move this method to it tb 2023-05-02
357+
private Array createResultArray(Number value, Number fillValue, DataType dataType, Interval interval) {
358+
final int windowHeight = interval.getY();
359+
final int windowWidth = interval.getX();
360+
final Array windowArray = NetCDFUtils.create(dataType,
361+
new int[]{windowHeight, windowWidth},
362+
fillValue);
363+
364+
final int windowCenterX = windowWidth / 2;
365+
final int windowCenterY = windowHeight / 2;
366+
windowArray.setObject(windowWidth * windowCenterY + windowCenterX, value);
367+
return windowArray;
368+
}
369+
}

0 commit comments

Comments
 (0)