OGS
VtkRaster.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
4#include "VtkRaster.h"
5
6#include <vtkBMPReader.h>
7#include <vtkImageData.h>
8#include <vtkImageImport.h>
9#include <vtkImageReader2.h>
10#include <vtkJPEGReader.h>
11#include <vtkPNGReader.h>
12#include <vtkTIFFReader.h>
13
14#include <QFileInfo>
15#include <algorithm>
16#include <cmath>
17#include <limits>
18
19#ifdef GEOTIFF_FOUND
20#include <geo_tiffp.h>
21#include <xtiffio.h>
22#endif
23
24#include <memory>
25
26#include "BaseLib/FileTools.h"
27#include "BaseLib/Logging.h"
28#include "BaseLib/StringTools.h"
30#include "GeoLib/Raster.h"
31
32vtkImageAlgorithm* VtkRaster::loadImage(const std::string& fileName)
33{
34 QFileInfo fileInfo(QString::fromStdString(fileName));
35
36 std::unique_ptr<GeoLib::Raster> raster(nullptr);
37 if (fileInfo.suffix().toLower() == "asc")
38 {
39 raster.reset(
41 }
42 else if (fileInfo.suffix().toLower() == "grd")
43 {
44 raster.reset(
46 }
47 else if (fileInfo.suffix().toLower() == "xyz")
48 {
49 raster.reset(
51 }
52 if (raster)
53 {
54 return VtkRaster::loadImageFromArray(raster->data(),
55 raster->getHeader());
56 }
57 if ((fileInfo.suffix().toLower() == "tif") ||
58 (fileInfo.suffix().toLower() == "tiff"))
59 {
60#ifdef GEOTIFF_FOUND
61 return loadImageFromTIFF(fileName);
62#else
63 ERR("VtkRaster::loadImage(): GeoTiff file format not supported in this "
64 "version! Trying to parse as Tiff-file.");
65 return loadImageFromFile(fileName);
66#endif
67 }
68
69 return loadImageFromFile(fileName);
70}
71vtkImageImport* VtkRaster::loadImageFromArray(double const* const data_array,
73{
74 const unsigned length = header.n_rows * header.n_cols * header.n_depth;
75 auto* data = new float[length * 2];
76 float max_val =
77 static_cast<float>(*std::max_element(data_array, data_array + length));
78 for (unsigned j = 0; j < length; ++j)
79 {
80 data[j * 2] = static_cast<float>(data_array[j]);
81 if (fabs(data[j * 2] - header.no_data) <
82 std::numeric_limits<double>::epsilon())
83 {
84 data[j * 2] = max_val;
85 data[j * 2 + 1] = 0;
86 }
87 else
88 {
89 data[j * 2 + 1] = max_val;
90 }
91 }
92
93 vtkImageImport* image = vtkImageImport::New();
94 image->SetDataSpacing(header.cell_size, header.cell_size, header.cell_size);
95 image->SetDataOrigin(header.origin[0] + (header.cell_size / 2.0),
96 header.origin[1] + (header.cell_size / 2.0),
97 0); // translate whole mesh by half a pixel in x and y
98 image->SetWholeExtent(0, header.n_cols - 1, 0, header.n_rows - 1, 0,
99 header.n_depth - 1);
100 image->SetDataExtent(0, header.n_cols - 1, 0, header.n_rows - 1, 0,
101 header.n_depth - 1);
102 image->SetDataExtentToWholeExtent();
103 image->SetDataScalarTypeToFloat();
104 image->SetNumberOfScalarComponents(2);
105 image->SetImportVoidPointer(data, 0);
106 image->Update();
107
108 return image;
109}
110
111#ifdef GEOTIFF_FOUND
112vtkImageAlgorithm* VtkRaster::loadImageFromTIFF(const std::string& fileName)
113{
114 TIFF* tiff = XTIFFOpen(fileName.c_str(), "r");
115
116 if (tiff)
117 {
118 GTIF* geoTiff = GTIFNew(tiff);
119
120 int version[3];
121 int count(0);
122 GTIFDirectoryInfo(geoTiff, version, &count);
123
124 if (count == 0)
125 WARN("VtkRaster::loadImageFromTIFF - file is not georeferenced.");
126
127 if (geoTiff)
128 {
129 double x0 = 0.0;
130 double y0 = 0.0;
131 double cellsize = 1.0;
132 int imgWidth = 0;
133 int imgHeight = 0;
134 int nImages = 0;
135 int pntCount = 0;
136 double* pnts = nullptr;
137
138 // get actual number of images in the tiff file
139 do
140 {
141 ++nImages;
142 } while (TIFFReadDirectory(tiff));
143 if (nImages > 1)
144 INFO(
145 "VtkRaster::loadImageFromTIFF() - File contains {:d} "
146 "images. This method is not tested for this case.",
147 nImages);
148
149 // get image size
150 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &imgWidth);
151 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &imgHeight);
152
153 // get cellsize
154 // Note: GeoTiff allows anisotropic pixels. This is not supported
155 // here and equilateral pixels are assumed.
156 if (TIFFGetField(tiff, GTIFF_PIXELSCALE, &pntCount, &pnts))
157 {
158 if (pnts[0] != pnts[1])
159 WARN(
160 "VtkRaster::loadImageFromTIFF(): Original raster data "
161 "has anisotrop pixel size!");
162 cellsize = pnts[0];
163 }
164
165 // get upper left point / origin
166 if (TIFFGetField(tiff, GTIFF_TIEPOINTS, &pntCount, &pnts))
167 {
168 x0 = pnts[3];
169 y0 = pnts[4] -
170 (imgHeight * cellsize); // the origin should be the lower
171 // left corner of the img
172 }
173
174 // read pixel values
175 auto* pixVal = static_cast<uint32*>(
176 _TIFFmalloc(imgWidth * imgHeight * sizeof(uint32)));
177 if ((imgWidth > 0) && (imgHeight > 0))
178 {
179 if (!TIFFReadRGBAImage(tiff, imgWidth, imgHeight, pixVal, 0))
180 {
181 ERR("VtkRaster::loadImageFromTIFF(): reading GeoTIFF "
182 "file.");
183 _TIFFfree(pixVal);
184 GTIFFree(geoTiff);
185 XTIFFClose(tiff);
186 return nullptr;
187 }
188 }
189
190 // check for colormap
191 uint16 photometric;
192 TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
193 // read colormap
194 uint16 *cmap_red = nullptr, *cmap_green = nullptr,
195 *cmap_blue = nullptr;
196 int colormap_used = TIFFGetField(tiff, TIFFTAG_COLORMAP, &cmap_red,
197 &cmap_green, &cmap_blue);
198
199 auto* data = new float[imgWidth * imgHeight * 4];
200 auto* pxl(new int[4]);
201 for (int j = 0; j < imgHeight; ++j)
202 {
203 int lineindex = j * imgWidth;
204 for (int i = 0; i < imgWidth; ++i)
205 { // scale intensities and set nodata values to white (i.e. the
206 // background colour)
207 unsigned pxl_idx(lineindex + i);
208 unsigned pos = 4 * (pxl_idx);
209 if (photometric == 1 && colormap_used == 1)
210 {
211 int idx = TIFFGetR(pixVal[pxl_idx]);
212 data[pos] = static_cast<float>(cmap_red[idx] >> 8);
213 data[pos + 1] =
214 static_cast<float>(cmap_green[idx] >> 8);
215 data[pos + 2] = static_cast<float>(cmap_blue[idx] >> 8);
216 data[pos + 3] = 1;
217 }
218 else
219 {
220 data[pos] =
221 static_cast<float>(TIFFGetR(pixVal[pxl_idx]));
222 data[pos + 1] =
223 static_cast<float>(TIFFGetG(pixVal[pxl_idx]));
224 data[pos + 2] =
225 static_cast<float>(TIFFGetB(pixVal[pxl_idx]));
226 data[pos + 3] =
227 static_cast<float>(TIFFGetA(pixVal[pxl_idx]));
228 }
229 }
230 }
231 delete[] pxl;
232
233 // set transparency values according to maximum pixel value
234 if (photometric == 1)
235 {
236 float max_val(0);
237 unsigned nPixels = 4 * imgWidth * imgHeight;
238 for (unsigned j = 0; j < nPixels; ++j)
239 {
240 if (data[j] > max_val)
241 {
242 max_val = data[j];
243 }
244 }
245
246 for (unsigned j = 0; j < nPixels; j += 4)
247 {
248 data[j + 3] = max_val;
249 }
250 }
251
252 vtkImageImport* image = vtkImageImport::New();
253 image->SetDataOrigin(x0, y0, 0);
254 image->SetDataSpacing(cellsize, cellsize, cellsize);
255 image->SetWholeExtent(0, imgWidth - 1, 0, imgHeight - 1, 0, 0);
256 image->SetDataExtent(0, imgWidth - 1, 0, imgHeight - 1, 0, 0);
257 image->SetDataExtentToWholeExtent();
258 image->SetDataScalarTypeToFloat();
259 image->SetNumberOfScalarComponents(4);
260 image->SetImportVoidPointer(data, 0);
261 image->Update();
262
263 _TIFFfree(pixVal);
264 GTIFFree(geoTiff);
265 XTIFFClose(tiff);
266 return image;
267 }
268
269 XTIFFClose(tiff);
270 ERR("VtkRaster::loadImageFromTIFF() - File not recognised as "
271 "GeoTIFF-Image.");
272 return nullptr;
273 }
274
275 ERR("VtkRaster::loadImageFromTIFF() - File not recognised as TIFF-Image.");
276 return nullptr;
277}
278#endif
279
280vtkImageReader2* VtkRaster::loadImageFromFile(const std::string& fileName)
281{
282 QString file_name(QString::fromStdString(fileName));
283 QFileInfo fi(file_name);
284 vtkImageReader2* image(nullptr);
285
286 if (fi.suffix().toLower() == "png")
287 {
288 image = vtkPNGReader::New();
289 }
290 else if ((fi.suffix().toLower() == "tif") ||
291 (fi.suffix().toLower() == "tiff"))
292 {
293 image = vtkTIFFReader::New();
294 }
295 else if ((fi.suffix().toLower() == "jpg") ||
296 (fi.suffix().toLower() == "jpeg"))
297 {
298 image = vtkJPEGReader::New();
299 }
300 else if (fi.suffix().toLower() == "bmp")
301 {
302 image = vtkBMPReader::New();
303 }
304 else
305 {
306 ERR("VtkRaster::readImageFromFile(): File format not supported, please "
307 "convert to BMP, JPG, PNG or TIFF.");
308 return nullptr;
309 }
310
311 image->SetFileName(fileName.c_str());
312 image->GetOutput()->AllocateScalars(VTK_FLOAT, 1);
313 image->Update();
314 readWorldFile(fileName, image);
315 return image;
316}
317
318std::string VtkRaster::findWorldFile(std::string const& filename)
319{
320 std::string const no_ext = BaseLib::dropFileExtension(filename);
321
322 constexpr std::array supported_extensions = {
323 ".pgw", ".pngw", ".pgwx", ".jgw", ".jpgw", ".jgwx", ".tfw",
324 ".tifw", ".tfwx", ".bpw", ".bmpw", ".bpwx", ".wld"};
325
326 auto const res =
327 std::find_if(supported_extensions.begin(), supported_extensions.end(),
328 [&no_ext](auto const& ext) -> bool
329 { return BaseLib::IsFileExisting(no_ext + ext); });
330 if (res != supported_extensions.end())
331 {
332 return no_ext + *res;
333 }
334
335 // no world file found
336 return {};
337}
338
339bool VtkRaster::readWorldFile(std::string const& filename,
340 vtkImageReader2* image)
341{
342 std::string const world_file = findWorldFile(filename);
343 if (world_file.empty())
344 {
345 WARN("No world file found. Image is not georeferenced.");
346 return false;
347 }
348
349 std::ifstream in(world_file.c_str());
350 if (!in.is_open())
351 {
352 ERR("VtkRaster::readWorldFile(): Could not open file {:s}.", filename);
353 return false;
354 }
355
356 std::string line;
357 // x-scaling
358 if (!std::getline(in, line))
359 {
360 return false;
361 }
362 double const delta_x = BaseLib::str2number<double>(line);
363 // 2x rotation (ignored)
364 if (!(std::getline(in, line) && std::getline(in, line)))
365 {
366 return false;
367 }
368 // negative y-scaling
369 if (!std::getline(in, line))
370 {
371 return false;
372 }
373 double const delta_y = BaseLib::str2number<double>(line);
374 if (delta_x != -delta_y)
375 WARN(
376 "Anisotropic pixel size detected ({:f} vs {:f}). An isotropic "
377 "spacing of {:f} is assumed, be aware results may be wrong.",
378 delta_x, delta_y, delta_x);
379 // x-translation
380 if (!std::getline(in, line))
381 {
382 return false;
383 }
384 double const x0 = BaseLib::str2number<double>(line);
385 // y-translation
386 if (!std::getline(in, line))
387 {
388 return false;
389 }
390 double const y0 = BaseLib::str2number<double>(line);
391
392 int extent[3];
393 image->GetOutput()->GetDimensions(extent);
394 image->SetDataSpacing(delta_x, delta_x, delta_x);
395 // for GIS the origin is on the lower left, for VTK it is on the upper left
396 double const vtk_y0 = y0 + (extent[1] * delta_y);
397 image->SetDataOrigin(x0, vtk_y0, 0);
398 return true;
399}
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
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 GeoLib::Raster * getRasterFromASCFile(std::string const &fname)
Reads an ArcGis ASC raster file.
static std::string findWorldFile(const std::string &filename)
static vtkImageAlgorithm * loadImageFromTIFF(const std::string &fileName)
static vtkImageReader2 * loadImageFromFile(const std::string &fileName)
static bool readWorldFile(std::string const &filename, vtkImageReader2 *image)
static vtkImageImport * loadImageFromArray(double const *const data_array, GeoLib::RasterHeader header)
Returns a VtkImageAlgorithm from an array of pixel values and some image meta data.
Definition VtkRaster.cpp:71
static vtkImageAlgorithm * loadImage(const std::string &fileName)
Loads an image- or raster-file into an vtkImageAlgorithm-Object.
Definition VtkRaster.cpp:32
std::string dropFileExtension(std::string const &filename)
T str2number(const std::string &str)
Definition StringTools.h:53
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