1 /*!
2 *
3 * \file read_input.cpp
4 * \brief Reads non-GIS input files
5 * \details TODO A more detailed description of these routines.
6 * \author Andres Payo
7 * \author David Favis-Mortlock
8 * \author Martin Husrt
9 * \author Monica Palaseanu-Lovejoy
10 * \date 2017
11 * \copyright GNU General Public License
12 *
13 */
14
15 /*==============================================================================================================================
16
17 This file is part of CliffMetrics, the Coastal Modelling Environment.
18
19 CliffMetrics is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 ==============================================================================================================================*/
26 #include <stdlib.h> // for atof()
27 #include <fstream>
28 using std::ifstream;
29
30 #include <iostream>
31 using std::cout;
32 using std::cerr;
33 using std::endl;
34 using std::ios;
35
36 #include "cliffmetrics.h"
37 #include "delineation.h"
38
39
40 /*==============================================================================================================================
41
42 The bReadIni member function reads the initialization file
43
44 ==============================================================================================================================*/
bReadIni(void)45 bool CDelineation::bReadIni(void)
46 {
47 m_strCLIFFIni = m_strCLIFFDir;
48 m_strCLIFFIni.append(CLIFF_INI);
49
50 // The .ini file is assumed to be in the CliffMetrics executable's directory
51 string strFilePathName(m_strCLIFFIni);
52
53 // Tell the user what is happening
54 cout << READFILELOC << strFilePathName << endl;
55
56 // Create an ifstream object
57 ifstream InStream;
58
59 // Try to open .ini file for input
60 InStream.open(strFilePathName.c_str(), ios::in);
61
62 // Did it open OK?
63 if (! InStream.is_open())
64 {
65 // Error: cannot open .ini file for input
66 cerr << ERR << "cannot open " << strFilePathName << " for input" << endl;
67 return false;
68 }
69
70 char szRec[BUFSIZE] = "";
71 int i = 0;
72 string strRec, strErr;
73
74 while (InStream.getline(szRec, BUFSIZE, '\n'))
75 {
76 strRec = szRec;
77
78 // Trim off leading and trailing whitespace
79 strRec = strTrimLeft(&strRec);
80 strRec = strTrimRight(&strRec);
81
82 // If it is a blank line or a comment then ignore it
83 if ((! strRec.empty()) && (strRec[0] != QUOTE1) && (strRec[0] != QUOTE2))
84 {
85 // It isn't so increment counter
86 i++;
87
88 // Find the colon: note that lines MUST have a colon separating data from leading description portion
89 size_t nPos = strRec.find(':');
90 if (nPos == string::npos)
91 {
92 // Error: badly formatted line (no colon)
93 cerr << ERR << "badly formatted line (no ':') in " << strFilePathName << endl << szRec << endl;
94 return false;
95 }
96
97 if (nPos == strRec.size()-1)
98 {
99 // Error: badly formatted line (colon with nothing following)
100 cerr << ERR << "badly formatted line (nothing following ':') in " << strFilePathName << endl << szRec << endl;
101 return false;
102 }
103
104 // Strip off leading portion (the bit up to and including the colon)
105 string strRH = strRec.substr(nPos+1);
106
107 // Remove leading whitespace
108 strRH = strTrimLeft(&strRH);
109
110 // Look for a trailing comment, if found then terminate string at that point and trim off any trailing whitespace
111 nPos = strRH.rfind(QUOTE1);
112 if (nPos != string::npos)
113 strRH = strRH.substr(0, nPos+1);
114
115 nPos = strRH.rfind(QUOTE2);
116 if (nPos != string::npos)
117 strRH = strRH.substr(0, nPos+1);
118
119 // Remove trailing whitespace
120 strRH = strTrimRight(&strRH);
121
122 switch (i)
123 {
124 case 1:
125 // The main input run-data filename
126 if (strRH.empty())
127 strErr = "path and name of main datafile";
128 else
129 {
130 // First check that we don't already have an input run-data filename, e.g. one entered on the command-line
131 if (m_strDataPathName.empty())
132 {
133 // We don't: so first check for leading slash, or leading Unix home dir symbol, or occurrence of a drive letter
134 if ((strRH[0] == PATH_SEPARATOR) || (strRH[0] == '~') || (strRH[1] == ':'))
135 // It has an absolute path, so use it 'as is'
136 m_strDataPathName = strRH;
137 else
138 {
139 // It has a relative path, so prepend the CliffMetrics dir
140 m_strDataPathName = m_strCLIFFDir;
141 m_strDataPathName.append(strRH);
142 }
143 }
144 }
145 break;
146
147 case 2:
148 // Path for CliffMetrics output
149 if (strRH.empty())
150 strErr = "path for CliffMetrics output";
151 else
152 {
153 // Check for trailing slash on CliffMetrics output directory name (is vital)
154 if (strRH[strRH.size()-1] != PATH_SEPARATOR)
155 strRH.append(&PATH_SEPARATOR);
156
157 // Now check for leading slash, or leading Unix home dir symbol, or occurrence of a drive letter
158 if ((strRH[0] == PATH_SEPARATOR) || (strRH[0] == '~') || (strRH[1] == ':'))
159 // It is an absolute path, so use it 'as is'
160 m_strOutPath = strRH;
161 else
162 {
163 // It is a relative path, so prepend the CliffMetrics dir
164 m_strOutPath = m_strCLIFFDir;
165 m_strOutPath.append(strRH);
166 }
167 }
168 break;
169
170 case 3:
171 // Email address, only useful if running under Linux/Unix
172 if (! strRH.empty())
173 {
174 // Something was entered, do rudimentary check for valid email address
175 if (strRH.find('@') == string::npos)
176 strErr = "email address for messages";
177 else
178 m_strMailAddress = strRH;
179 }
180 break;
181 }
182
183 // Did an error occur?
184 if (! strErr.empty())
185 {
186 // Error in input to initialisation file
187 cerr << ERR << "reading " << strErr << " in " << strFilePathName << endl << "'" << strRec << "'" << endl;
188 InStream.close();
189
190 return false;
191 }
192 }
193 }
194
195 InStream.close();
196 return true;
197 }
198
199
200 /*==============================================================================================================================
201
202 Reads the run details input file and does some initialization
203
204 ==============================================================================================================================*/
bReadRunData(void)205 bool CDelineation::bReadRunData(void)
206 {
207 // Create an ifstream object
208 ifstream InStream;
209
210 // Try to open run details file for input
211 InStream.open(m_strDataPathName.c_str(), ios::in);
212
213 // Did it open OK?
214 if (! InStream.is_open())
215 {
216 // Error: cannot open run details file for input
217 cerr << ERR << "cannot open " << m_strDataPathName << " for input" << endl;
218 return false;
219 }
220
221 char szRec[BUFSIZE] = "";
222 int
223 i = 0;
224 size_t nPos = 0;
225 string strRec, strErr;
226
227 while (InStream.getline(szRec, BUFSIZE, '\n'))
228 {
229 strRec = szRec;
230
231 // Trim off leading and trailing whitespace
232 strRec = strTrimLeft(&strRec);
233 strRec = strTrimRight(&strRec);
234
235 // If it is a blank line or a comment then ignore it
236 if ((! strRec.empty()) && (strRec[0] != QUOTE1) && (strRec[0] != QUOTE2))
237 {
238 // It isn't so increment counter
239 i++;
240
241 // Find the colon: note that lines MUST have a colon separating data from leading description portion
242 nPos = strRec.find(':');
243 if (nPos == string::npos)
244 {
245 // Error: badly formatted line (no colon)
246 cerr << ERR << "badly formatted line (no ':') in " << m_strDataPathName << endl << szRec << endl;
247 return false;
248 }
249
250 // Strip off leading portion (the bit up to and including the colon)
251 string strRH = strRec.substr(nPos+1);
252
253 // Remove leading whitespace after the colon
254 strRH = strTrimLeft(&strRH);
255
256 // Look for trailing comments, if found then terminate string at that point and trim off any trailing whitespace
257 bool bFound = true;
258 while (bFound)
259 {
260 bFound = false;
261
262 nPos = strRH.rfind(QUOTE1);
263 if (nPos != string::npos)
264 {
265 strRH = strRH.substr(0, nPos);
266 bFound = true;
267 }
268
269 nPos = strRH.rfind(QUOTE2);
270 if (nPos != string::npos)
271 {
272 strRH = strRH.substr(0, nPos);
273 bFound = true;
274 }
275
276 // Trim trailing spaces
277 strRH = strTrimRight(&strRH);
278 }
279
280 #ifdef _WIN32
281 // For Windows, make sure has backslashes, not Unix-style slashes
282 strRH = pstrChangeToBackslash(&strRH);
283 #endif
284
285 string strTmp;
286
287 switch (i)
288 {
289 // ---------------------------------------------- Run Information -----------------------------------------------------
290 case 1:
291 // Text output file names, don't change case
292 if (strRH.empty())
293 strErr = "output file names";
294 else
295 {
296 m_strRunName = strRH;
297
298 m_strOutFile = m_strOutPath;
299 m_strOutFile.append(strRH);
300 m_strOutFile.append(OUTEXT);
301
302 m_strLogFile = m_strOutPath;
303 m_strLogFile.append(strRH);
304 m_strLogFile.append(LOGEXT);
305 }
306 break;
307
308 case 2:
309 // DTM file (can't be blank)
310 if (! strRH.empty())
311 {
312 #ifdef _WIN32
313 // For Windows, make sure has backslashes, not Unix-style slashes
314 strRH = pstrChangeToBackslash(&strRH);
315 #endif
316 // Now check for leading slash, or leading Unix home dir symbol, or occurrence of a drive letter
317 if ((strRH[0] == PATH_SEPARATOR) || (strRH[0] == '~') || (strRH[1] == ':'))
318 // It has an absolute path, so use it 'as is'
319 m_strDTMFile = strRH;
320 else
321 {
322 // It has a relative path, so prepend the CliffMetrics dir
323 m_strDTMFile = m_strCLIFFDir;
324 m_strDTMFile.append(strRH);
325 }
326 }
327 break;
328
329 case 3:
330 // Still water level (m) used to extract the shoreline
331 m_dStillWaterLevel = atof(strRH.c_str());
332 break;
333
334 case 4:
335 // Vector coastline smoothing algorithm: 0 = none, 1 = running mean, 2 = Savitsky-Golay
336 m_nCoastSmooth = atoi(strRH.c_str());
337 if ((m_nCoastSmooth < SMOOTH_NONE) || (m_nCoastSmooth > SMOOTH_SAVITZKY_GOLAY))
338 strErr = "coastline vector smoothing algorithm";
339 break;
340
341 case 5:
342 // Size of coastline smoothing window: must be odd
343 m_nCoastSmoothWindow = atoi(strRH.c_str());
344 if ((m_nCoastSmoothWindow <= 0) || !(m_nCoastSmoothWindow % 2))
345 strErr = "size of coastline vector smoothing window (must be > 0 and odd)";
346 break;
347
348 case 6:
349 // Order of coastline profile smoothing polynomial for Savitsky-Golay: usually 2 or 4, max is 6
350 m_nSavGolCoastPoly = atoi(strRH.c_str());
351 if ((m_nSavGolCoastPoly <= 0) || (m_nSavGolCoastPoly > 6))
352 strErr = "value of Savitsky-Golay polynomial for coastline smoothing (must be <= 6)";
353 break;
354
355 case 7:
356 // Optional shoreline shape file (can be blank)
357 if (! strRH.empty())
358 {
359 #ifdef _WIN32
360 // For Windows, make sure has backslashes, not Unix-style slashes
361 strRH = pstrChangeToBackslash(&strRH);
362 #endif
363 // Now check for leading slash, or leading Unix home dir symbol, or occurrence of a drive letter
364 if ((strRH[0] == PATH_SEPARATOR) || (strRH[0] == '~') || (strRH[1] == ':'))
365 {
366 // It has an absolute path, so use it 'as is'
367 m_strInitialCoastlineFile = strRH;
368 }
369 else
370 {
371 // It has a relative path, so prepend the CliffMetrics dir
372 m_strInitialCoastlineFile = m_strCLIFFDir;
373 m_strInitialCoastlineFile.append(strRH);
374 }
375 }
376 break;
377
378 case 8:
379 if (! m_strInitialCoastlineFile.empty())
380 {
381 m_nCoastSeaHandiness = atoi(strRH.c_str());
382 }
383 case 9:
384 // Raster GIS output format (note must retain original case). Blank means use same format as input DEM file (if possible)
385 m_strRasterGISOutFormat = strTrimLeft(&strRH);
386 break;
387
388 case 10:
389 // If needed, also output GIS raster world file?
390 strRH = strToLower(&strRH);
391
392 m_bWorldFile = false;
393 if (strRH.find("y") != string::npos)
394 m_bWorldFile = true;
395 break;
396
397 case 11:
398 // If needed, scale GIS raster output values?
399 strRH = strToLower(&strRH);
400
401 m_bScaleRasterOutput = false;
402 if (strRH.find("y") != string::npos)
403 m_bScaleRasterOutput = true;
404 break;
405
406 case 12:
407 // Vector GIS output format (note must retain original case)
408 m_strVectorGISOutFormat = strRH;
409
410 if (strRH.empty())
411 strErr = "vector GIS output format";
412 break;
413
414 case 13:
415 // Random edge for coastline search?
416 strRH = strToLower(&strRH);
417
418 m_bRandomCoastEdgeSearch = false;
419 if (strRH.find("y") != string::npos)
420 m_bRandomCoastEdgeSearch = true;
421 break;
422
423 case 14:
424 // Random number seed(s)
425 m_ulRandSeed[0] = atol(strRH.c_str());
426 if (0 == m_ulRandSeed[0])
427 {
428 strErr = "random number seed";
429 break;
430 }
431 // TODO rewrite this, similar to reading raster slice elevations
432 // OK, so find out whether we're dealing with a single seed or more than one: check for a space
433 nPos = strRH.find(SPACE);
434 if (nPos != string::npos)
435 {
436 // There's a space, so we must have more than one number
437 int n = 0;
438 do
439 {
440 // Trim off the part before the first space then remove leading whitespace
441 strRH = strRH.substr(nPos, strRH.size()-nPos);
442 strRH = strTrimLeft(&strRH);
443
444 // Put the number into the array
445 m_ulRandSeed[++n] = atol(strRH.c_str());
446
447 // Now look for another space
448 nPos = strRH.find(SPACE);
449 }
450 while ((n < NRNG) && (nPos != string::npos));
451 }
452 else
453 {
454 // Only one seed specified, so make all seeds the same
455 for (int n = 1; n < NRNG; n++)
456 m_ulRandSeed[n] = m_ulRandSeed[n-1];
457 }
458 break;
459
460 case 15:
461 // Length of coastline normals (m)
462 m_dCoastNormalLength = atof(strRH.c_str());
463 if (m_dCoastNormalLength <= 0)
464 strErr = "length of coastline normals must be greater than zero";
465 break;
466
467 case 16:
468 // Vertical tolerance avoid false CliffTops/Toes
469 m_dEleTolerance = atof(strRH.c_str());
470 if (m_dEleTolerance <= 0)
471 strErr = "vertical elevation tolerance must be greater than zero";
472 break;
473 }
474
475 // Did an error occur?
476 if (! strErr.empty())
477 {
478 // Error in input to run details file
479 cerr << endl << ERR << strErr << ".\nPlease edit " << m_strDataPathName << " and change this line:" << endl << "'" << szRec << "'" << endl << endl;
480 InStream.close();
481 return false;
482 }
483 }
484 }
485 // Close file
486 InStream.close();
487
488 return true;
489 }
490
491