OGS
AsciiRasterInterface.cpp
Go to the documentation of this file.
1
15
16#include <fstream>
17#include <tuple>
18
19#include "BaseLib/FileTools.h"
20#include "BaseLib/Logging.h"
21#include "BaseLib/StringTools.h"
22#include "GeoLib/Point.h"
23
24namespace FileIO
25{
27{
28 std::string ext(BaseLib::getFileExtension(fname));
29 std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
30 if (ext == ".asc")
31 {
32 return getRasterFromASCFile(fname);
33 }
34 if (ext == ".grd")
35 {
36 return getRasterFromSurferFile(fname);
37 }
38 if (ext == ".xyz")
39 {
40 return getRasterFromXyzFile(fname);
41 }
42 return nullptr;
43}
44
46static double readDoubleFromStream(std::istream& in)
47{
48 std::string value;
49 in >> value;
50 return std::strtod(BaseLib::replaceString(",", ".", value).c_str(),
51 nullptr);
52}
53
56static std::optional<GeoLib::RasterHeader> readASCHeader(std::ifstream& in)
57{
59
60 std::string tag;
61 std::string value;
62
63 in >> tag;
64 if (tag == "ncols")
65 {
66 in >> value;
67 header.n_cols = atoi(value.c_str());
68 }
69 else
70 {
71 return {};
72 }
73
74 in >> tag;
75 if (tag == "nrows")
76 {
77 in >> value;
78 header.n_rows = atoi(value.c_str());
79 }
80 else
81 {
82 return {};
83 }
84
85 header.n_depth = 1;
86
87 in >> tag;
88 if (tag == "xllcorner" || tag == "xllcenter")
89 {
90 header.origin[0] = readDoubleFromStream(in);
91 }
92 else
93 {
94 return {};
95 }
96
97 in >> tag;
98 if (tag == "yllcorner" || tag == "yllcenter")
99 {
100 header.origin[1] = readDoubleFromStream(in);
101 }
102 else
103 {
104 return {};
105 }
106 header.origin[2] = 0;
107
108 in >> tag;
109 if (tag == "cellsize")
110 {
111 header.cell_size = readDoubleFromStream(in);
112 }
113 else
114 {
115 return {};
116 }
117
118 in >> tag;
119 if (tag == "NODATA_value" || tag == "nodata_value")
120 {
121 header.no_data = readDoubleFromStream(in);
122 }
123 else
124 {
125 return {};
126 }
127
128 return header;
129}
130
132 std::string const& fname)
133{
134 std::ifstream in(fname.c_str());
135
136 if (!in.is_open())
137 {
138 WARN("Raster::getRasterFromASCFile(): Could not open file {:s}.",
139 fname);
140 return nullptr;
141 }
142
143 auto const header = readASCHeader(in);
144 if (!header)
145 {
146 WARN(
147 "Raster::getRasterFromASCFile(): Could not read header of file "
148 "{:s}",
149 fname);
150 return nullptr;
151 }
152
153 std::vector<double> values(header->n_cols * header->n_rows);
154 // read the data into the double-array
155 for (std::size_t j(0); j < header->n_rows; ++j)
156 {
157 const std::size_t idx((header->n_rows - j - 1) * header->n_cols);
158 for (std::size_t i(0); i < header->n_cols; ++i)
159 {
160 values[idx + i] = readDoubleFromStream(in);
161 }
162 }
163
164 return new GeoLib::Raster(*header, values.begin(), values.end());
165}
166
169static std::optional<std::tuple<GeoLib::RasterHeader, double, double>>
170readSurferHeader(std::ifstream& in)
171{
172 std::string tag;
173
174 in >> tag;
175
176 if (tag != "DSAA")
177 {
178 ERR("Error in readSurferHeader() - No Surfer file.");
179 return {};
180 }
181
183 in >> header.n_cols >> header.n_rows;
184 double min, max;
185 in >> min >> max;
186 header.origin[0] = min;
187 header.cell_size = (max - min) / static_cast<double>(header.n_cols);
188
189 in >> min >> max;
190 header.origin[1] = min;
191 header.origin[2] = 0;
192
193 if (ceil((max - min) / static_cast<double>(header.n_rows)) ==
194 ceil(header.cell_size))
195 {
196 header.cell_size = ceil(header.cell_size);
197 }
198 else
199 {
200 ERR("Error in readSurferHeader() - Anisotropic cellsize detected.");
201 return {};
202 }
203 header.n_depth = 1;
204 header.no_data = -9999;
205 in >> min >> max;
206
207 return {{header, min, max}};
208}
209
211 std::string const& fname)
212{
213 std::ifstream in(fname.c_str());
214
215 if (!in.is_open())
216 {
217 ERR("Raster::getRasterFromSurferFile() - Could not open file {:s}",
218 fname);
219 return nullptr;
220 }
221
222 auto const optional_header = readSurferHeader(in);
223 if (!optional_header)
224 {
225 ERR("Raster::getRasterFromASCFile() - could not read header of file "
226 "{:s}",
227 fname);
228 return nullptr;
229 }
230
231 auto const [header, min, max] = *optional_header;
232 std::vector<double> values(header.n_cols * header.n_rows);
233 // read the data into the double-array
234 for (std::size_t j(0); j < header.n_rows; ++j)
235 {
236 const std::size_t idx(j * header.n_cols);
237 for (std::size_t i(0); i < header.n_cols; ++i)
238 {
239 const double val = readDoubleFromStream(in);
240 values[idx + i] = (val > max || val < min) ? header.no_data : val;
241 }
242 }
243
244 return new GeoLib::Raster(header, values.begin(), values.end());
245}
246
247std::vector<std::string> readFile(std::istream& in)
248{
249 std::vector<std::string> lines;
250 std::string line("");
251 while (std::getline(in, line))
252 {
253 lines.emplace_back(line);
254 }
255 return lines;
256}
257
258std::optional<std::array<double, 3>> readXyzCoordinates(std::string const& line)
259{
260 std::array<double, 3> coords = {0, 0, 0};
261 if (auto const n = std::sscanf(line.c_str(), "%lf %lf %lf", &coords[0],
262 &coords[1], &coords[2]);
263 n != 3)
264 {
265 ERR("Raster::readXyzCoordinates() - Read {:d} doubles out of 3 "
266 "expected from the following line:\n{:s}",
267 n, line);
268 return std::nullopt;
269 }
270 return coords;
271}
272
273GeoLib::RasterHeader getXyzHeader(std::vector<std::string> const& lines)
274{
275 double x_min = std::numeric_limits<double>::max();
276 double x_max = -std::numeric_limits<double>::max();
277 double y_min = std::numeric_limits<double>::max();
278 double y_max = -std::numeric_limits<double>::max();
279 double cellsize = std::numeric_limits<double>::max();
280 std::optional<std::array<double, 3>> coords;
281
282 std::size_t const n_lines(lines.size());
283 for (std::size_t i = 0; i < n_lines; ++i)
284 {
285 coords = readXyzCoordinates(lines[i]);
286 if (coords == std::nullopt)
287 {
288 MathLib::Point3d org(std::array<double, 3>{{0, 0, 0}});
289 GeoLib::RasterHeader fail = {0, 0, 0, org, 0, 0};
290 return fail;
291 }
292 double const diff = (*coords)[0] - x_min;
293 if (diff > 0)
294 {
295 cellsize = std::min(cellsize, diff);
296 }
297 x_min = std::min((*coords)[0], x_min);
298 x_max = std::max((*coords)[0], x_max);
299 y_min = std::min((*coords)[1], y_min);
300 y_max = std::max((*coords)[1], y_max);
301 }
302
304 header.cell_size = cellsize;
305 header.no_data = -9999;
306 header.n_cols = static_cast<std::size_t>(((x_max - x_min) / cellsize) + 1);
307 header.n_rows = static_cast<std::size_t>(((y_max - y_min) / cellsize) + 1);
308 header.n_depth = 1;
309 header.origin[0] = x_min;
310 header.origin[1] = y_min;
311 return header;
312}
313
315 std::string const& fname)
316{
317 std::ifstream in(fname.c_str());
318 if (!in.is_open())
319 {
320 ERR("Raster::getRasterFromXyzFile() - Could not open file {:s}", fname);
321 return nullptr;
322 }
323
324 auto const string_lines = readFile(in);
325 in.close();
326
327 auto const header = getXyzHeader(string_lines);
328 if (header.n_cols == 0 && header.n_rows == 0)
329 {
330 return nullptr;
331 }
332
333 std::optional<std::array<double, 3>> coords;
334 std::vector<double> values(header.n_cols * header.n_rows, -9999);
335 std::size_t const n_lines(string_lines.size());
336 for (std::size_t i = 0; i < n_lines; ++i)
337 {
338 coords = readXyzCoordinates(string_lines[i]);
339 std::size_t const idx = static_cast<std::size_t>(
340 (header.n_cols *
341 (((*coords)[1] - header.origin[1]) / header.cell_size)) +
342 (((*coords)[0] - header.origin[0]) / header.cell_size));
343 values[idx] = (*coords)[2];
344 }
345 return new GeoLib::Raster(header, values.begin(), values.end());
346}
347
349 std::string const& file_name)
350{
351 GeoLib::RasterHeader header(raster.getHeader());
352 MathLib::Point3d const& origin(header.origin);
353 unsigned const nCols(header.n_cols);
354 unsigned const nRows(header.n_rows);
355
356 // write header
357 std::ofstream out(file_name);
358 out << "ncols " << nCols << "\n";
359 out << "nrows " << nRows << "\n";
360 auto const default_precision = out.precision();
361 out.precision(std::numeric_limits<double>::max_digits10);
362 out << "xllcorner " << origin[0] << "\n";
363 out << "yllcorner " << origin[1] << "\n";
364 out << "cellsize " << header.cell_size << "\n";
365 out.precision(default_precision);
366 out << "NODATA_value " << header.no_data << "\n";
367
368 // write data
369 for (unsigned row(0); row < nRows; ++row)
370 {
371 for (unsigned col(0); col < nCols - 1; ++col)
372 {
373 out << raster.data()[(nRows - row - 1) * nCols + col] << " ";
374 }
375 out << raster.data()[(nRows - row) * nCols - 1] << "\n";
376 }
377 out.close();
378}
379
381static bool allRastersExist(std::vector<std::string> const& raster_paths)
382{
383 return std::all_of(raster_paths.begin(), raster_paths.end(),
384 [](std::string const& raster_path)
385 {
386 if (BaseLib::IsFileExisting(raster_path))
387 {
388 return true;
389 }
390 ERR("Opening raster file {} failed.", raster_path);
391 return false;
392 });
393}
394
395std::optional<std::vector<GeoLib::Raster const*>> readRasters(
396 std::vector<std::string> const& raster_paths)
397{
398 if (!allRastersExist(raster_paths))
399 {
400 return std::nullopt;
401 }
402
403 std::vector<GeoLib::Raster const*> rasters;
404 rasters.reserve(raster_paths.size());
405 std::transform(raster_paths.begin(), raster_paths.end(),
406 std::back_inserter(rasters),
407 [](auto const& path)
408 { return FileIO::AsciiRasterInterface::readRaster(path); });
409 return std::make_optional(rasters);
410}
411} // end namespace FileIO
Definition of the AsciiRasterInterface class.
Filename manipulation routines.
Definition of the Point class.
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
Definition of string helper functions.
static GeoLib::Raster * getRasterFromXyzFile(std::string const &fname)
Reads a XYZ raster file.
static GeoLib::Raster * getRasterFromSurferFile(std::string const &fname)
Reads a Surfer GRD raster file.
static void writeRasterAsASC(GeoLib::Raster const &raster, std::string const &file_name)
Writes an Esri asc-file.
static GeoLib::Raster * readRaster(std::string const &fname)
static GeoLib::Raster * getRasterFromASCFile(std::string const &fname)
Reads an ArcGis ASC raster file.
Class Raster is used for managing raster data.
Definition Raster.h:49
double const * data() const
Definition Raster.h:111
RasterHeader const & getHeader() const
Returns the complete header information.
Definition Raster.h:85
std::string getFileExtension(const std::string &path)
std::string replaceString(const std::string &searchString, const std::string &replaceString, std::string stringToReplace)
std::optional< std::array< double, 3 > > readXyzCoordinates(std::string const &line)
static std::optional< GeoLib::RasterHeader > readASCHeader(std::ifstream &in)
static double readDoubleFromStream(std::istream &in)
Reads a double replacing comma by point.
std::vector< std::string > readFile(std::istream &in)
GeoLib::RasterHeader getXyzHeader(std::vector< std::string > const &lines)
static std::optional< std::tuple< GeoLib::RasterHeader, double, double > > readSurferHeader(std::ifstream &in)
static bool allRastersExist(std::vector< std::string > const &raster_paths)
Checks if all raster files actually exist.
Contains the relevant information when storing a geoscientific raster data.
Definition Raster.h:28
std::size_t n_depth
Definition Raster.h:31
MathLib::Point3d origin
Definition Raster.h:32
std::size_t n_cols
Definition Raster.h:29
std::size_t n_rows
Definition Raster.h:30