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