Isis 3 Programmer Reference
TiffImporter.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include <iostream>
8#include <iomanip>
9
10#include <QDebug>
11#include <QDomDocument>
12#include <QDomElement>
13#include <QDomNode>
14
15#include "TiffImporter.h"
16
17#include "Angle.h"
18#include "FileName.h"
19#include "IException.h"
20#include "IString.h"
21#include "ProjectionFactory.h"
22#include "Pvl.h"
23#include "PvlGroup.h"
24#include "PvlToPvlTranslationManager.h"
25#include "TProjection.h"
26
27namespace Isis {
34
35 // Open the TIFF image
36 m_image = NULL;
37 if ((m_image = XTIFFOpen(inputName.expanded().toLatin1().data(), "r")) == NULL) {
39 QString("Could not open TIFF image [") + inputName.expanded().toLatin1().data() + "]", _FILEINFO_);
40 }
41
42 // Get its constant dimensions. Note, height seems to get reset to 0 if
43 // called before setting width.
44 uint32_t height;
45 TIFFGetField(m_image, TIFFTAG_IMAGELENGTH, &height);
46 setLines(height);
47
48 uint32_t width;
49 TIFFGetField(m_image, TIFFTAG_IMAGEWIDTH, &width);
50 setSamples(width);
51
52 TIFFGetField(m_image, TIFFTAG_SAMPLESPERPIXEL, &m_samplesPerPixel);
53
54 // Setup the width and height of the image
55 unsigned long imagesize = lines() * samples();
56 m_raster = NULL;
57 if ((m_raster = (uint32_t *) malloc(sizeof(uint32_t) * imagesize)) == NULL) {
59 "Could not allocate enough memory", _FILEINFO_);
60 }
61
62 // Read the image into the memory buffer
63 if (TIFFReadRGBAImage(m_image, samples(), lines(), m_raster, 0) == 0) {
65 "Could not read image", _FILEINFO_);
66 }
67
68 // Deal with photometric interpretations
69 if (TIFFGetField(m_image, TIFFTAG_PHOTOMETRIC, &m_photo) == 0) {
71 "Image has an undefined photometric interpretation", _FILEINFO_);
72 }
73
74 // Get the geotiff info (if available)
75 m_geotiff = NULL;
76 m_geotiff = GTIFNew(m_image);
77
79 }
80
81
86 free(m_raster);
87 m_raster = NULL;
88
89 GTIFFree(m_geotiff);
90 m_geotiff = NULL;
91
92 XTIFFClose(m_image);
93 m_image = NULL;
94 }
95
96
111
112 Pvl outPvl;
113 outPvl.addGroup(PvlGroup("Mapping"));
114
115 geocode_t modelType;
116 geocode_t rasterType;
117 geocode_t coordSysType;
118
119 if ((GTIFKeyGet(m_geotiff, GTModelTypeGeoKey, &modelType, 0, 1) == 1) &&
120 ((modelType == 1) || (modelType == 2))) { // 1=ModelTypeProjected, 2=ModelTypeGeographic
121
122 if ((GTIFKeyGet(m_geotiff, GTRasterTypeGeoKey, &rasterType, 0, 1) == 1) &&
123 (rasterType == 1 || rasterType == 2)) { // Area || Point
124
125 // See if this geogiff uses a coded projection. That is, the projection parameters are not
126 // explicitly set. A code in the GEOTIF tag indicates a set of parameters for this
127 // projection.
128 if (GTIFKeyGet(m_geotiff, ProjectedCSTypeGeoKey, &coordSysType, 0, 1) == 1) {
129
130 // Get the mapping group data for this code: proj name, clat, clon, ...
131 FileName transFile((QString) "$ISISROOT/appdata/translations/" +
132 toString(coordSysType) + ".trn");
133 if (transFile.fileExists()) {
134 Pvl tmp;
135 tmp += PvlKeyword("Code", toString(coordSysType));
136 PvlToPvlTranslationManager geoTiffCodeTranslater(tmp, transFile.expanded());
137 geoTiffCodeTranslater.Auto(outPvl);
138 }
139 }
140
141 // The following is here for when this gets generalized to handle non coded projections
142 // (i.e., the real proj parameters have values and the code is not used)
143 else if (1 == 0) {
144 geocode_t geoCode;
145 if (GTIFKeyGet(m_geotiff, GeographicTypeGeoKey, &geoCode, 0, 1) == 1) {
146 std::cout << "GeographicTypeGeoKey = " << geoCode << std::endl;
147 }
148 else {
149 std::cout << "no GeographicTypeGeoKey" << std::endl;
150 }
151
152 if (GTIFKeyGet(m_geotiff, GeogAngularUnitsGeoKey, &geoCode, 0, 1) == 1) {
153 std::cout << "GeogAngularUnitsGeoKey = " << geoCode << std::endl;
154 }
155 else {
156 std::cout << "no GeogAngularUnitsGeoKey" << std::endl;
157 }
158
159 if (GTIFKeyGet(m_geotiff, GeogEllipsoidGeoKey, &geoCode, 0, 1) == 1) {
160 std::cout << "GeogEllipsoidGeoKey = " << geoCode << std::endl;
161 }
162 else {
163 std::cout << "no GeogEllipsoidGeoKey" << std::endl;
164 }
165
166 if (GTIFKeyGet(m_geotiff, GeogSemiMajorAxisGeoKey, &geoCode, 0, 1) == 1) {
167 std::cout << "GeogSemiMajorAxisGeoKey = " << geoCode << std::endl;
168 }
169 else {
170 std::cout << "no GeogSemiMajorAxisGeoKey" << std::endl;
171 }
172
173 if (GTIFKeyGet(m_geotiff, GeogSemiMinorAxisGeoKey, &geoCode, 0, 1) == 1) {
174 std::cout << "GeogSemiMinorAxisGeoKey = " << geoCode << std::endl;
175 }
176 else {
177 std::cout << "no GeogSemiMinorAxisGeoKey" << std::endl;
178 }
179
180 if (GTIFKeyGet(m_geotiff, GeogInvFlatteningGeoKey, &geoCode, 0, 1) == 1) {
181 std::cout << "GeogInvFlatteningGeoKey = " << geoCode << std::endl;
182 }
183 else {
184 std::cout << "no GeogInvFlatteningGeoKey" << std::endl;
185 }
186
187 if (GTIFKeyGet(m_geotiff, ProjCoordTransGeoKey, &geoCode, 0, 1) == 1) {
188 std::cout << "ProjCoordTransGeoKey = " << geoCode << std::endl;
189 }
190 else {
191 std::cout << "no ProjCoordTransGeoKey" << std::endl;
192 }
193
194 if (GTIFKeyGet(m_geotiff, ProjLinearUnitsGeoKey, &geoCode, 0, 1) == 1) {
195 std::cout << "ProjLinearUnitsGeoKey = " << geoCode << std::endl;
196 }
197 else {
198 std::cout << "no ProjLinearUnitsGeoKey" << std::endl;
199 }
200
201 if (GTIFKeyGet(m_geotiff, ProjStdParallel1GeoKey, &geoCode, 0, 1) == 1) {
202 std::cout << "ProjStdParallel1GeoKey = " << geoCode << std::endl;
203 }
204 else {
205 std::cout << "no ProjStdParallel1GeoKey" << std::endl;
206 }
207
208 if (GTIFKeyGet(m_geotiff, ProjStdParallel2GeoKey, &geoCode, 0, 1) == 1) {
209 std::cout << "ProjStdParallel2GeoKey = " << geoCode << std::endl;
210 }
211 else {
212 std::cout << "no ProjStdParallel2GeoKey" << std::endl;
213 }
214
215 if (GTIFKeyGet(m_geotiff, ProjNatOriginLongGeoKey, &geoCode, 0, 1) == 1) {
216 std::cout << "ProjNatOriginLongGeoKey = " << geoCode << std::endl;
217 }
218 else {
219 std::cout << "no ProjNatOriginLongGeoKey" << std::endl;
220 }
221
222 if (GTIFKeyGet(m_geotiff, ProjNatOriginLatGeoKey, &geoCode, 0, 1) == 1) {
223 std::cout << "ProjNatOriginLatGeoKey = " << geoCode << std::endl;
224 }
225 else {
226 std::cout << "no ProjNatOriginLatGeoKey" << std::endl;
227 }
228
229 if (GTIFKeyGet(m_geotiff, ProjFalseEastingGeoKey, &geoCode, 0, 1) == 1) {
230 std::cout << "ProjFalseEastingGeoKey = " << geoCode << std::endl;
231 }
232 else {
233 std::cout << "no ProjFalseEastingGeoKey" << std::endl;
234 }
235
236 if (GTIFKeyGet(m_geotiff, ProjFalseNorthingGeoKey, &geoCode, 0, 1) == 1) {
237 std::cout << "ProjFalseNorthingGeoKey = " << geoCode << std::endl;
238 }
239 else {
240 std::cout << "no ProjFalseNorthingGeoKey" << std::endl;
241 }
242
243 if (GTIFKeyGet(m_geotiff, ProjFalseOriginLongGeoKey, &geoCode, 0, 1) == 1) {
244 std::cout << "ProjFalseOriginLongGeoKey = " << geoCode << std::endl;
245 }
246 else {
247 std::cout << "no ProjFalseOriginLongGeoKey" << std::endl;
248 }
249
250 if (GTIFKeyGet(m_geotiff, ProjFalseOriginLatGeoKey, &geoCode, 0, 1) == 1) {
251 std::cout << "ProjFalseOriginLatGeoKey = " << geoCode << std::endl;
252 }
253 else {
254 std::cout << "no ProjFalseOriginLatGeoKey" << std::endl;
255 }
256
257 if (GTIFKeyGet(m_geotiff, ProjFalseOriginEastingGeoKey, &geoCode, 0, 1) == 1) {
258 std::cout << "ProjFalseOriginEastingGeoKey = " << geoCode << std::endl;
259 }
260 else {
261 std::cout << "no ProjFalseOriginEastingGeoKey" << std::endl;
262 }
263
264 if (GTIFKeyGet(m_geotiff, ProjFalseOriginNorthingGeoKey, &geoCode, 0, 1) == 1) {
265 std::cout << "ProjFalseOriginNorthingGeoKey = " << geoCode << std::endl;
266 }
267 else {
268 std::cout << "no ProjFalseOriginNorthingGeoKey" << std::endl;
269 }
270
271 if (GTIFKeyGet(m_geotiff, ProjCenterLongGeoKey, &geoCode, 0, 1) == 1) {
272 std::cout << "ProjCenterLongGeoKey = " << geoCode << std::endl;
273 }
274 else {
275 std::cout << "no ProjCenterLongGeoKey" << std::endl;
276 }
277
278 if (GTIFKeyGet(m_geotiff, ProjCenterLatGeoKey, &geoCode, 0, 1) == 1) {
279 std::cout << "ProjCenterLatGeoKey = " << geoCode << std::endl;
280 }
281 else {
282 std::cout << "no ProjCenterLatGeoKey" << std::endl;
283 }
284
285 if (GTIFKeyGet(m_geotiff, ProjCenterEastingGeoKey, &geoCode, 0, 1) == 1) {
286 std::cout << "ProjCenterEastingGeoKey = " << geoCode << std::endl;
287 }
288 else {
289 std::cout << "no ProjCenterEastingGeoKey" << std::endl;
290 }
291
292 if (GTIFKeyGet(m_geotiff, ProjCenterNorthingGeoKey, &geoCode, 0, 1) == 1) {
293 std::cout << "ProjCenterNorthingGeoKey = " << geoCode << std::endl;
294 }
295 else {
296 std::cout << "no ProjCenterNorthingGeoKey" << std::endl;
297 }
298
299 if (GTIFKeyGet(m_geotiff, ProjScaleAtNatOriginGeoKey, &geoCode, 0, 1) == 1) {
300 std::cout << "ProjScaleAtNatOriginGeoKey = " << geoCode << std::endl;
301 }
302 else {
303 std::cout << "no ProjScaleAtNatOriginGeoKey" << std::endl;
304 }
305
306 if (GTIFKeyGet(m_geotiff, ProjAzimuthAngleGeoKey, &geoCode, 0, 1) == 1) {
307 std::cout << "ProjAzimuthAngleGeoKey = " << geoCode << std::endl;
308 }
309 else {
310 std::cout << "no ProjAzimuthAngleGeoKey" << std::endl;
311 }
312
313 if (GTIFKeyGet(m_geotiff, ProjStraightVertPoleLongGeoKey, &geoCode, 0, 1) == 1) {
314 std::cout << "ProjStraightVertPoleLongGeoKey = " << geoCode << std::endl;
315 }
316 else {
317 std::cout << "no ProjStraightVertPoleLongGeoKey" << std::endl;
318 }
319
320 if (GTIFKeyGet(m_geotiff, VerticalUnitsGeoKey, &geoCode, 0, 1) == 1) {
321 std::cout << "VerticalUnitsGeoKey = " << geoCode << std::endl;
322 }
323 else {
324 std::cout << "no VerticalUnitsGeoKey" << std::endl;
325 }
326 } // End of individual GEOTIFF projection parameters
327
328 // There are some projections parameters that are not in the GEO part of the TIFF, get them
329
330 // Get the Tiff Tiepoint tag and convert those to Upper Left X & Y
331 outPvl = upperLeftXY(outPvl);
332
333 // Get the Tiff PixelScale tag and convert it to resolution and scale
334 outPvl = resolution(outPvl);
335
336 // Get the GDAL minimum and maximum lats and lons
337 outPvl = gdalItems(outPvl);
338
339 } // End of raster type
340
341
342 // Test the projection
343 // If any things goes wrong just ignore the projection and move on
344 try {
345 TProjection *proj = NULL;
346 proj = (TProjection *) ProjectionFactory::Create(outPvl);
347
348 PvlGroup &mapGroup = outPvl.findGroup("Mapping");
349 double pixelResolution = mapGroup["PixelResolution"];
350 double trueScaleLat = proj->TrueScaleLatitude();
351 double localRadius = proj->LocalRadius(trueScaleLat);
352
353 double scale = (2.0 * Isis::PI * localRadius) / (360.0 * pixelResolution);
354 mapGroup += PvlKeyword("Scale", toString(scale), "pixels/degree");
355 }
356 catch (IException &e) {
357 outPvl.findGroup("Mapping").clear();
358 }
359 }
360
361 return outPvl.findGroup("Mapping");
362 }
363
364
371 Pvl TiffImporter::gdalItems(const Pvl &inLab) const {
372
373 Pvl newLab = inLab;
374 PvlGroup &map = newLab.findGroup("Mapping");
375
376 // Get the GDALMetadata tag (42112) to get the lat/lon boundry
377 char *gdalMetadataBuf;
378 short int gdalMetadataCount = 0;
379
380 if (TIFFGetField(m_image, 42112, &gdalMetadataCount, &gdalMetadataBuf) == 1) {
381
382 QString gdalMetadataQstring(gdalMetadataBuf);
383
384 QDomDocument gdalDoc("GDALMetaData");
385 if (gdalDoc.setContent(gdalMetadataQstring)) {
386 QDomElement gdalRoot = gdalDoc.documentElement();
387 if (gdalRoot.tagName() == "GDALMetadata") {
388
389 QDomNode gdalNode = gdalRoot.firstChild();
390 while (!gdalNode.isNull()) {
391 QDomElement gdalElement = gdalNode.toElement();
392 if (!gdalElement.isNull() ) {
393 if (gdalElement.tagName() == "Item") {
394 if (gdalElement.attribute("name", "") == "WEST_LONGITUDE") {
395 QString westLon = gdalElement.text();
396 map += PvlKeyword("MinimumLongitude", toString(Angle(westLon).degrees()));
397 }
398 else if (gdalElement.attribute("name", "") == "EAST_LONGITUDE") {
399 QString eastLon = gdalElement.text();
400 map += PvlKeyword("MaximumLongitude", toString(Angle(eastLon).degrees()));
401 }
402 else if (gdalElement.attribute("name", "") == "SOUTH_LATITUDE") {
403 QString southLat = gdalElement.text();
404 map += PvlKeyword("MinimumLatitude", toString(Angle(southLat).degrees()));
405 }
406 else if (gdalElement.attribute("name", "") == "NORTH_LATITUDE") {
407 QString northLat = gdalElement.text();
408 map += PvlKeyword("MaximumLatitude", toString(Angle(northLat).degrees()));
409 }
410 }
411 }
412
413 gdalNode = gdalNode.nextSibling();
414 }
415 }
416 }
417 }
418
419 return newLab;
420 }
421
422
430 Pvl TiffImporter::upperLeftXY(const Pvl &inLab) const {
431
432 Pvl newLab = inLab;
433 PvlGroup &map = newLab.findGroup("Mapping");
434
435 double *tiePoints = NULL;
436 short int tieCount = 0;
437 if (TIFFGetField(m_image, TIFFTAG_GEOTIEPOINTS, &tieCount, &tiePoints) == 1) {
438
439 // The expected tiepoints are TIFF(i, j, k, x, y, z) = ISIS(sample, line, 0, X, Y, 0)
440 // Make sure the (x, y) refer to the (0, 0)
441 if (tiePoints[0] == 0.0 && tiePoints[1] == 0.0) {
442 double x = 0.0;
443 if (map.hasKeyword("FalseEasting")) {
444 x = (double)map["FalseEasting"] + tiePoints[3];
445 map.deleteKeyword("FalseEasting");
446 }
447
448 double y = 0.0;
449 if (map.hasKeyword("FalseNorthing")) {
450 y = (double)map["FalseNorthing"] + tiePoints[4];
451 map.deleteKeyword("FalseNorthing");
452 }
453
454 map += PvlKeyword("UpperLeftCornerX", toString(x), "meters");
455 map += PvlKeyword("UpperLeftCornerY", toString(y), "meters");
456 }
457 else {
458 QString msg = "The upper left X and Y can not be calculated. Unsupported tiepoint "
459 "type in Tiff file (i.e., not ( 0.0, 0.0))";
460 throw IException(IException::User, msg, _FILEINFO_);
461 }
462 }
463
464 return newLab;
465 }
466
467
475 Pvl TiffImporter::resolution(const Pvl &inLab) const {
476
477 Pvl newLab = inLab;
478 PvlGroup &map = newLab.findGroup("Mapping");
479
480 // Get the Tiff PixelScale tag and convert it to resolution
481 double *scales = NULL;
482 short int scaleCount = 0;
483 if (TIFFGetField(m_image, TIFFTAG_GEOPIXELSCALE, &scaleCount, &scales) == 1) {
484
485 // The expected scales are TIFF(x, y, z) = ISIS(sample, line, 0)
486 // Make sure the (x, y) are the same but not zero (0) for ISIS
487 if ((scaleCount == 3) && (scales[0] > 0.0 && scales[1] > 0.0) && (scales[0] == scales[1])) {
488 map += PvlKeyword("PixelResolution", toString(scales[0]), "meters");
489 }
490 else {
491 QString msg = "The pixel resolution could not be retrieved from the TIFF file. Unsupported "
492 "PixelScale tag values.";
493 throw IException(IException::User, msg, _FILEINFO_);
494 }
495 }
496
497 return newLab;
498 }
499
500
511 return m_samplesPerPixel;
512 }
513
514
522 return
523 m_photo == PHOTOMETRIC_MINISWHITE ||
524 m_photo == PHOTOMETRIC_MINISBLACK;
525 }
526
527
534 bool TiffImporter::isRgb() const {
535 return !isGrayscale() && samplesPerPixel() <= 3;
536 }
537
538
545 bool TiffImporter::isArgb() const {
546 return !isGrayscale() && samplesPerPixel() > 3;
547 }
548
549
557 void TiffImporter::updateRawBuffer(int line, int band) const {
558 }
559
560
571 int TiffImporter::getPixel(int s, int l) const {
572 l = lines() - l - 1;
573 int index = l * samples() + s;
574 return m_raster[index];
575 }
576
577
587 int TiffImporter::getGray(int pixel) const {
588 return convertRgbToGray(pixel);
589 }
590
591
599 int TiffImporter::getRed(int pixel) const {
600 return TIFFGetR(pixel);
601 }
602
603
611 int TiffImporter::getGreen(int pixel) const {
612 return TIFFGetG(pixel);
613 }
614
615
623 int TiffImporter::getBlue(int pixel) const {
624 return TIFFGetB(pixel);
625 }
626
627
635 int TiffImporter::getAlpha(int pixel) const {
636 return TIFFGetA(pixel);
637 }
638};
Defines an angle and provides unit conversions.
Definition Angle.h:45
File name manipulation and expansion.
Definition FileName.h:100
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
Imports images with standard formats into Isis as cubes.
void setLines(int l)
Set the line dimension (height) of the output image.
int samples() const
The sample dimension (width) of the output image.
void setSamples(int s)
Set the sample dimension (width) of the output image.
virtual int convertRgbToGray(int pixel) const
Convert the current pixel, taken from an RGB/A image, and blend its RGB components into a single gray...
void setDefaultBands()
Set the number of bands to be created for the output cube based on the number of color channels in th...
int lines() const
The line dimension (height) of the output image.
static Isis::Projection * Create(Isis::Pvl &label, bool allowDefaults=false)
This method returns a pointer to a Projection object.
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
A single keyword-value pair.
Definition PvlKeyword.h:87
void addGroup(const Isis::PvlGroup &group)
Add a group to the object.
Definition PvlObject.h:186
Allows applications to translate simple text files.
Base class for Map TProjections.
TIFF * m_image
LibTIFF representation of the input image.
GTIF * m_geotiff
GeoTiff hanele.
virtual int getBlue(int pixel) const
Retrieves the blue component of the given pixel.
virtual ~TiffImporter()
Destruct the importer.
int samplesPerPixel() const
The number of "samples" (bands in Isis terms) per pixel in the input image.
Pvl upperLeftXY(const Pvl &inLab) const
Convert the Tiff Tiepoint tag data to Upper Left X & Y values for the ISIS cube label mapping group.
virtual bool isRgb() const
Tests to see if the input image is neither grayscale nor has more than three samples per pixel,...
virtual bool isArgb() const
Tests to see if the input image is not grayscale and has more than three samples per pixel,...
TiffImporter(FileName inputName)
Construct the importer.
uint16_t m_samplesPerPixel
The number of "samples" (bands in Isis terms) in the input image.
virtual int getGray(int pixel) const
Retrieves the gray component of the given pixel.
virtual int getGreen(int pixel) const
Retrieves the green component of the given pixel.
uint32_t * m_raster
Buffer holding the raw TIFF image in memory.
virtual int getAlpha(int pixel) const
Retrieves the alpha component of the given pixel.
uint16_t m_photo
The enumerated photometric interpretation of the input image.
virtual int getRed(int pixel) const
Retrieves the red component of the given pixel.
Pvl resolution(const Pvl &inLab) const
Convert the Tiff PixelScale tag data to a singe resolution for the ISIS cube label mapping group.
virtual bool isGrayscale() const
Tests to see if the input image has a "min is white" or "min is black" photometric interpretation,...
virtual int getPixel(int s, int l) const
Returns a representation of a pixel for the input format that can then be broken down into specific g...
Pvl gdalItems(const Pvl &outPvl) const
Convert items in the GDAL tag to the ISIS mapping group.
virtual PvlGroup convertProjection() const
Convert any projection information associated with the input image to an ISIS Mapping group in PVL fo...
virtual void updateRawBuffer(int line, int band) const
Does nothing as LibTIFF reads the entire input image into memory, and therefore does not need to be u...
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
const double PI
The mathematical constant PI.
Definition Constants.h:40