outputchannel.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file outputchannel.cpp
  3. /// \brief Classes for formatting and printing output from the model
  4. ///
  5. /// \author Joe Siltberg
  6. /// $Date: 2013-10-10 10:20:33 +0200 (Thu, 10 Oct 2013) $
  7. ///
  8. ///////////////////////////////////////////////////////////////////////////////////////
  9. #include "config.h"
  10. #include "guess.h"
  11. #include "outputchannel.h"
  12. #include <vector>
  13. namespace GuessOutput {
  14. ColumnDescriptor::ColumnDescriptor(const char* title,
  15. int width,
  16. int precision)
  17. : t(title),
  18. w(width),
  19. p(precision) {
  20. }
  21. const std::string& ColumnDescriptor::title() const {
  22. return t;
  23. }
  24. int ColumnDescriptor::width() const {
  25. return w;
  26. }
  27. int ColumnDescriptor::precision() const {
  28. return p;
  29. }
  30. ColumnDescriptors::ColumnDescriptors(const std::vector<std::string>& titles,
  31. int width,
  32. int precision) {
  33. for (size_t i = 0; i < titles.size(); i++) {
  34. columns.push_back(ColumnDescriptor(titles[i].c_str(),
  35. width,
  36. precision));
  37. }
  38. }
  39. void ColumnDescriptors::operator+=(const ColumnDescriptor& col) {
  40. columns.push_back(col);
  41. }
  42. void ColumnDescriptors::operator+=(const ColumnDescriptors& cols) {
  43. columns.insert(columns.end(), cols.columns.begin(), cols.columns.end());
  44. }
  45. size_t ColumnDescriptors::size() const {
  46. return columns.size();
  47. }
  48. const ColumnDescriptor& ColumnDescriptors::operator[](size_t i) const {
  49. return columns[i];
  50. }
  51. TableDescriptor::TableDescriptor(const char* name,
  52. const ColumnDescriptors& columns)
  53. : n(name),
  54. cols(columns) {
  55. }
  56. const std::string& TableDescriptor::name() const {
  57. return n;
  58. }
  59. const ColumnDescriptors& TableDescriptor::columns() const {
  60. return cols;
  61. }
  62. Table::Table()
  63. : identifier(-1) {
  64. }
  65. Table::Table(int id)
  66. : identifier(id) {
  67. }
  68. int Table::id() const {
  69. return identifier;
  70. }
  71. bool Table::invalid() const {
  72. return identifier == -1;
  73. }
  74. Table OutputChannel::create_table(const TableDescriptor& descriptor) {
  75. Table table((int) table_descriptors.size());
  76. table_descriptors.push_back(descriptor);
  77. values.resize(table_descriptors.size());
  78. return table;
  79. }
  80. void OutputChannel::add_value(const Table& table, double d) {
  81. // do nothing for unused tables
  82. if (table.invalid()) {
  83. return;
  84. }
  85. // make sure this row isn't already full
  86. const TableDescriptor& td = table_descriptors[table.id()];
  87. if (values[table.id()].size() == td.columns().size()) {
  88. fail("Added too many values to a row in table %s!",
  89. td.name().c_str());
  90. }
  91. values[table.id()].push_back(d);
  92. }
  93. const TableDescriptor&
  94. OutputChannel::get_table_descriptor(const Table& table) const {
  95. return table_descriptors[table.id()];
  96. }
  97. const std::vector<double>
  98. OutputChannel::get_current_row(const Table& table) const {
  99. return values[table.id()];
  100. }
  101. void OutputChannel::clear_current_row(const Table& table) {
  102. values[table.id()].clear();
  103. std::vector<double>().swap(values[table.id()]);
  104. }
  105. FileOutputChannel::FileOutputChannel(const char* out_dir,
  106. int coords_precision)
  107. : output_directory(out_dir) {
  108. // calculate suitable width for the coords columns,
  109. // longitudes take at most 4 characters (-180) before the decimal
  110. // point, add the decimal point, coords_precision and a little margin:
  111. const int LON_MAX_LEN = 4;
  112. const int MARGIN = 2;
  113. int coords_width = LON_MAX_LEN+1+coords_precision+MARGIN;
  114. xtring str;
  115. str.printf("%%%ds", coords_width);
  116. coords_title_format = (char*)str;
  117. str.printf("%%%d.%df", coords_width, coords_precision);
  118. coords_format = (char*)str;
  119. }
  120. FileOutputChannel::~FileOutputChannel() {
  121. for (size_t i = 0; i < files.size(); i++) {
  122. fclose(files[i]);
  123. }
  124. }
  125. Table FileOutputChannel::create_table(const TableDescriptor& descriptor) {
  126. Table table;
  127. FILE* file = NULL;
  128. if (descriptor.name() != "") {
  129. std::string full_path = output_directory + descriptor.name();
  130. file = fopen(full_path.c_str(), "w");
  131. if (file == NULL) {
  132. fail("Could not open %s for output\n"\
  133. "Close the file if it is open in another application",
  134. full_path.c_str());
  135. }
  136. else {
  137. table = OutputChannel::create_table(descriptor);
  138. files.push_back(file);
  139. printed_header.push_back(false);
  140. }
  141. }
  142. return table;
  143. }
  144. void FileOutputChannel::finish_row(const Table& table,
  145. double lon,
  146. double lat,
  147. int year) {
  148. finish_row(table, lon, lat, year, NOEXTRA, 0);
  149. }
  150. void FileOutputChannel::close_table(Table& table) {
  151. // do nothing for unused tables
  152. if (table.invalid()) {
  153. return;
  154. }
  155. FILE* file = files[table.id()];
  156. fclose(file);
  157. }
  158. void FileOutputChannel::finish_row(const Table& table,
  159. double lon,
  160. double lat,
  161. int year,
  162. columntype ctype,
  163. int ecvalue) {
  164. // do nothing for unused tables
  165. if (table.invalid()) {
  166. return;
  167. }
  168. FILE* file = files[table.id()];
  169. // make sure all columns have been added
  170. const std::vector<double>& row = get_current_row(table);
  171. const TableDescriptor& td = get_table_descriptor(table);
  172. if (row.size() < td.columns().size()) {
  173. fail("Too few values in a row in table %s", td.name().c_str());
  174. }
  175. // print the header if this is the first output for this file
  176. if (!printed_header[table.id()]) {
  177. // print title for coordinates and time columns
  178. fprintf(file, coords_title_format.c_str(), "Lon");
  179. fprintf(file, coords_title_format.c_str(), "Lat");
  180. fprintf(file, "%6s", "Year");
  181. if (ctype == CTDAY) {
  182. fprintf(file, "%5s", "Day");
  183. }
  184. else if (ctype == CTMONTH) {
  185. fprintf(file, "%5s", "Mth");
  186. }
  187. // print each column title
  188. int nbr_cols = (int) get_table_descriptor(table).columns().size();
  189. for (int i = 0; i < nbr_cols; i++) {
  190. fputs(format_header(table, i), file);
  191. }
  192. fprintf(file, "\n");
  193. printed_header[table.id()] = true;
  194. }
  195. // print out coordinates and time
  196. fprintf(file, coords_format.c_str(), lon);
  197. fprintf(file, coords_format.c_str(), lat);
  198. fprintf(file, "%6d", year);
  199. if (ctype != NOEXTRA) {
  200. fprintf(file, "%5d", ecvalue);
  201. }
  202. // print out the values
  203. for (size_t i = 0; i < row.size(); i++) {
  204. fprintf(file, format(table, (int)i), row[i]);
  205. }
  206. fprintf(file, "\n");
  207. fflush(file);
  208. // start on a new row
  209. clear_current_row(table);
  210. }
  211. const char* FileOutputChannel::format(const Table& table, int column) {
  212. const TableDescriptor& td = get_table_descriptor(table);
  213. const ColumnDescriptor& cd = td.columns()[column];
  214. // NB: not thread safe
  215. static char buf[100];
  216. sprintf(buf, " %%%d.%df", cd.width(), cd.precision());
  217. return buf;
  218. }
  219. const char* FileOutputChannel::format_header(const Table& table, int column) {
  220. const TableDescriptor& td = get_table_descriptor(table);
  221. const ColumnDescriptor& cd = td.columns()[column];
  222. // NB: not thread safe
  223. static char format[100];
  224. static char buf[100];
  225. sprintf(format, " %%%ds", cd.width());
  226. sprintf(buf, format, cd.title().c_str());
  227. return buf;
  228. }
  229. OutputRows::OutputRows(OutputChannel* output_channel,
  230. double longitude,
  231. double latitude,
  232. int year)
  233. : out(output_channel),
  234. lon(longitude),
  235. lat(latitude),
  236. y(year),
  237. c(NOEXTRA),
  238. v(0) {
  239. }
  240. OutputRows::OutputRows(OutputChannel* output_channel,
  241. double longitude,
  242. double latitude,
  243. int year,
  244. columntype ctype,
  245. int ecvalue)
  246. : out(output_channel),
  247. lon(longitude),
  248. lat(latitude),
  249. y(year),
  250. c(ctype),
  251. v(ecvalue) {
  252. }
  253. OutputRows::~OutputRows() {
  254. for (size_t i = 0; i < used_tables.size(); i++) {
  255. if (used_tables[i]) {
  256. if (c == NOEXTRA) {
  257. out->finish_row(Table(i), lon, lat, y);
  258. }
  259. else {
  260. out->finish_row(Table(i), lon, lat, y, c, v);
  261. }
  262. }
  263. }
  264. }
  265. void OutputRows::add_value(const Table& table,double d) {
  266. // do nothing for unused tables
  267. if (table.invalid()) {
  268. return;
  269. }
  270. int id = table.id();
  271. // remember that this table has gotten a value
  272. if (id >= static_cast<int>(used_tables.size())) {
  273. used_tables.resize(id+1);
  274. }
  275. used_tables[id] = true;
  276. // send the value to the output channel
  277. out->add_value(table, d);
  278. }
  279. }