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