33import com .bc .fiduceo .FiduceoConstants ;
44import com .bc .fiduceo .log .FiduceoLogger ;
55import com .bc .fiduceo .util .NetCDFUtils ;
6+ import com .bc .fiduceo .util .TimeUtils ;
67import org .apache .commons .cli .CommandLine ;
78import org .apache .commons .cli .HelpFormatter ;
89import org .apache .commons .cli .Option ;
910import org .apache .commons .cli .Options ;
1011import org .esa .snap .core .datamodel .GeoPos ;
12+ import org .esa .snap .core .util .StringUtils ;
13+ import org .esa .snap .core .util .io .FileUtils ;
1114import ucar .ma2 .Array ;
1215import ucar .ma2 .IndexIterator ;
1316import ucar .ma2 .InvalidRangeException ;
@@ -29,9 +32,6 @@ class MmdQCTool {
2932
3033 private final static Logger logger = FiduceoLogger .getLogger ();
3134
32- private FileMessages fileMessages ;
33- private MatchupAccumulator matchupAccumulator ;
34-
3535 // package access for testing only tb 2023-02-14
3636 static Options getOptions () {
3737 final Options options = new Options ();
@@ -43,17 +43,20 @@ static Options getOptions() {
4343 inputOption .setRequired (true );
4444 options .addOption (inputOption );
4545
46+ final Option outputOption = new Option ("o" , "outdir" , true , "Defines the result output directory." );
47+ options .addOption (outputOption );
48+
4649 final Option timeOption = new Option ("t" , "time" , true , "Defines matchup time variable name." );
4750 timeOption .setRequired (true );
4851 options .addOption (timeOption );
4952
50- final Option plotOption = new Option ("p" , "plot" , false , "Allows plotting the matchup locations onto a global map. Requires 'lon' and 'lat' to be set." );
53+ final Option plotOption = new Option ("p" , "plot" , false , "Enables plotting the matchup locations onto a global map. Requires 'lon' and 'lat' to be set." );
5154 options .addOption (plotOption );
5255
53- final Option lonOption = new Option ("lon" , "longitude" , false , "Defines the variable name for the longitude." );
56+ final Option lonOption = new Option ("lon" , "longitude" , true , "Defines the variable name for the longitude." );
5457 options .addOption (lonOption );
5558
56- final Option latOption = new Option ("lat" , "latitude" , false , "Defines the variable name for the latitude." );
59+ final Option latOption = new Option ("lat" , "latitude" , true , "Defines the variable name for the latitude." );
5760 options .addOption (latOption );
5861
5962 return options ;
@@ -75,7 +78,14 @@ static void writeReport(OutputStream outputStream, MatchupAccumulator accumulato
7578 final int size = messageMap .size ();
7679 writer .println (size + " file(s) with errors" );
7780 if (size > 0 ) {
78- // @todo 1 tb/tb add error messages per file here
81+ for (Map .Entry <String , List <String >> entry : messageMap .entrySet ()) {
82+ writer .println (entry .getKey ());
83+
84+ final List <String > messages = entry .getValue ();
85+ for (final String message : messages ) {
86+ writer .println (" - " + message );
87+ }
88+ }
7989 }
8090
8191 writer .println ();
@@ -96,14 +106,21 @@ void run(CommandLine commandLine) throws IOException {
96106 final List <Path > mmdFiles = getInputFiles (inputDirOption );
97107 logger .info ("Found " + mmdFiles .size () + " input file(s) to analyze." );
98108
99- fileMessages = new FileMessages ();
100- matchupAccumulator = new MatchupAccumulator ();
109+ final String outDirString = commandLine .getOptionValue ("o" );
110+ File outDir ;
111+ if (StringUtils .isNullOrEmpty (outDirString )) {
112+ outDir = new File ("." );
113+ } else {
114+ outDir = new File (outDirString );
115+ }
101116
117+ final FileMessages fileMessages = new FileMessages ();
118+ final MatchupAccumulator matchupAccumulator = new MatchupAccumulator ();
102119
103120 // loop over files
104121 for (final Path mmdFile : mmdFiles ) {
105-
106122 try (final NetcdfFile netcdfFile = NetCDFUtils .openReadOnly (mmdFile .toAbsolutePath ().toString ())) {
123+ matchupAccumulator .countFile ();
107124
108125 // read time variable center pixel
109126 final String timeVariableName = commandLine .getOptionValue ("t" );
@@ -116,41 +133,60 @@ void run(CommandLine commandLine) throws IOException {
116133 }
117134
118135 if (commandLine .hasOption ("p" )) {
119- final GlobalPlot filePlot = GlobalPlot .create ();
136+ plotMatchups (mmdFile , netcdfFile , commandLine , outDir );
137+ }
120138
121- final String mmdFilePath = mmdFile .toString ();
122- int dotIndex = mmdFilePath .lastIndexOf ("." );
123- final String pngFilePath = mmdFilePath .substring (0 , dotIndex + 1 ).concat ("png" );
139+ } catch (IOException | InvalidRangeException ioException ) {
140+ fileMessages .add (mmdFile .getFileName ().toString (), ioException .getMessage ());
141+ }
142+ }
124143
125- final Array latitudes = NetCDFUtils .getCenterPosArrayFromMMDFile (netcdfFile , "driftercmems-sirds_latitude" , null ,
126- null , FiduceoConstants .MATCHUP_COUNT );
144+ final Date now = TimeUtils .createNow ();
145+ final String timeString = TimeUtils .format (now , "yyyy-MM-dd" );
146+ final String qcFileName = "mmd_qc_report_" + timeString + ".txt" ;
147+ final File reportFile = new File (outDir , qcFileName );
148+ if (!reportFile .createNewFile ()) {
149+ throw new IOException ("unable to create report file: " + reportFile .getAbsolutePath ());
150+ }
151+ try (FileOutputStream outStream = new FileOutputStream (reportFile )) {
152+ writeReport (outStream , matchupAccumulator , fileMessages );
153+ }
154+ }
127155
128- final Array longitudes = NetCDFUtils .getCenterPosArrayFromMMDFile (netcdfFile , "driftercmems-sirds_longitude" , null ,
129- null , FiduceoConstants .MATCHUP_COUNT );
156+ private static void plotMatchups (Path mmdFile , NetcdfFile netcdfFile , CommandLine commandLine , File outDir ) throws IOException , InvalidRangeException {
157+ final String lonVariable = commandLine .getOptionValue ("lon" );
158+ final String latVariable = commandLine .getOptionValue ("lat" );
130159
131- int numMatches = latitudes .getShape ()[0 ];
132- final ArrayList <GeoPos > pointList = new ArrayList <>();
133- for (int i = 0 ; i < numMatches ; i ++) {
134- final GeoPos geoPos = new GeoPos (latitudes .getFloat (i ), longitudes .getFloat (i ));
135- pointList .add (geoPos );
136- }
160+ if (StringUtils .isNullOrEmpty (lonVariable ) || StringUtils .isNullOrEmpty (latVariable )) {
161+ throw new IllegalArgumentException ("must provide lon and lat variable names for plotting" );
162+ }
137163
138- filePlot .plot (pointList );
139- filePlot .writeTo (pngFilePath );
140- filePlot .dispose ();
141- }
164+ final String filenameWithoutExtension = FileUtils .getFilenameWithoutExtension (mmdFile .toFile ());
165+ final String pngFile = filenameWithoutExtension .concat (".png" );
166+ final File pngFilePath = new File (outDir , pngFile );
142167
143- } catch (IOException | InvalidRangeException ioException ) {
144- fileMessages .add (mmdFile .getFileName ().toString (), ioException .getMessage ());
145- }
168+ final GlobalPlot filePlot = GlobalPlot .create ();
169+ final Array latitudes = NetCDFUtils .getCenterPosArrayFromMMDFile (netcdfFile , latVariable , null ,
170+ null , FiduceoConstants .MATCHUP_COUNT );
171+
172+ final Array longitudes = NetCDFUtils .getCenterPosArrayFromMMDFile (netcdfFile , lonVariable , null ,
173+ null , FiduceoConstants .MATCHUP_COUNT );
174+
175+ int numMatches = 1 ;
176+ int [] shape = latitudes .getShape ();
177+ if (shape .length > 0 ) {
178+ numMatches = shape [0 ];
146179 }
147180
148- // write report
149- final TreeMap <String , Integer > treeMap = new TreeMap <>(matchupAccumulator .getDaysMap ());
150- final Set <Map .Entry <String , Integer >> entries = treeMap .entrySet ();
151- for (final Map .Entry <String , Integer > entry : entries ) {
152- System .out .println (entry .getKey () + ": " + entry .getValue ());
181+ final ArrayList <GeoPos > pointList = new ArrayList <>();
182+ for (int i = 0 ; i < numMatches ; i ++) {
183+ final GeoPos geoPos = new GeoPos (latitudes .getFloat (i ), longitudes .getFloat (i ));
184+ pointList .add (geoPos );
153185 }
186+
187+ filePlot .plot (pointList );
188+ filePlot .writeTo (pngFilePath .getAbsolutePath ());
189+ filePlot .dispose ();
154190 }
155191
156192 private List <Path > getInputFiles (String inputDirOption ) throws IOException {
0 commit comments