1 //***************************************************************
2 // CLass: %CLASS%
3 //
4 // Description:
5 //
6 //
7 // Author: Chris Browet <cbro@semperpax.com> (C) 2010
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //******************************************************************
12
13 #include "GdalAdapter.h"
14
15 #include <QCoreApplication>
16 #include <QtPlugin>
17 #include <QAction>
18 #include <QFileDialog>
19 #include <QPainter>
20 #include <QMessageBox>
21 #include <QInputDialog>
22
23 #include <QDebug>
24
25 #include "gdal_priv.h"
26 #include "ogrsf_frmts.h"
27
28 #include "ProjectionChooser.h"
29
30 #define IN_MEMORY_LIMIT 100000000
31
32 static const QUuid theUid ("{5c9479df-0b1a-4c49-9559-83d5ffa93911}");
33 static const QString theName("GDAL Raster");
34
getId() const35 QUuid GdalAdapterFactory::getId() const
36 {
37 return theUid;
38 }
39
getName() const40 QString GdalAdapterFactory::getName() const
41 {
42 return theName;
43 }
44
45 /**************/
46
radToAng(double a)47 inline double radToAng(double a)
48 {
49 return a*180/M_PI;
50 }
51
angToRad(double a)52 inline double angToRad(double a)
53 {
54 return a*M_PI/180.;
55 }
56
57 #define FILTER_OPEN_SUPPORTED \
58 tr("All Files (*)")
59
GdalAdapter()60 GdalAdapter::GdalAdapter()
61 : poDataset(0), isLatLon(false)
62 {
63 GDALAllRegister();
64
65 QAction* loadImage = new QAction(tr("Load file(s)..."), this);
66 loadImage->setData(theUid.toString());
67 connect(loadImage, SIGNAL(triggered()), SLOT(onLoadImage()));
68 QAction* setSource = new QAction(tr("Specify \"source\" tag..."), this);
69 setSource->setData(theUid.toString());
70 connect(setSource, SIGNAL(triggered()), SLOT(onSetSourceTag()));
71
72 theMenu = new QMenu();
73 theMenu->addAction(loadImage);
74 theMenu->addAction(setSource);
75 }
76
77
~GdalAdapter()78 GdalAdapter::~GdalAdapter()
79 {
80 cleanup();
81 }
82
getId() const83 QUuid GdalAdapter::getId() const
84 {
85 return theUid;
86 }
87
getName() const88 QString GdalAdapter::getName() const
89 {
90 return theName;
91 }
92
alreadyLoaded(QString fn) const93 bool GdalAdapter::alreadyLoaded(QString fn) const
94 {
95 for (int j=0; j<theImages.size(); ++j)
96 if (theImages[j].theFilename == fn)
97 return true;
98 return false;
99 }
100
loadImage(const QString & fn)101 bool GdalAdapter::loadImage(const QString& fn)
102 {
103 if (alreadyLoaded(fn))
104 return true;
105
106 QFileInfo fi(fn);
107 GdalImage img;
108 QRectF bbox;
109
110 poDataset = (GDALDataset *) GDALOpen( QDir::toNativeSeparators(fi.absoluteFilePath()).toUtf8().constData(), GA_ReadOnly );
111 if( poDataset == NULL )
112 {
113 qDebug() << "GDAL Open failed: " << fn;
114 return false;
115 }
116
117 bool hasGeo = false;
118 QDir dir(fi.absoluteDir());
119 QString f = fi.baseName();
120 QStringList wldFilter;
121 wldFilter << f+".tfw" << f+".tifw" << f+".tiffw" << f+".wld";
122 QFileInfoList fil = dir.entryInfoList(wldFilter);
123 if (fil.count()) {
124 QFile wld(fil[0].absoluteFilePath());
125 if (wld.open(QIODevice::ReadOnly)) {
126 int i;
127 for (i=0; i<6; ++i) {
128 if (wld.atEnd())
129 break;
130 QString l = wld.readLine();
131 bool ok;
132 double d = l.toDouble(&ok);
133 if (!ok)
134 break;
135 switch (i) {
136 case 0:
137 img.adfGeoTransform[1] = d;
138 break;
139 case 1:
140 img.adfGeoTransform[4] = d;
141 break;
142 case 2:
143 img.adfGeoTransform[2] = d;
144 break;
145 case 3:
146 img.adfGeoTransform[5] = d;
147 break;
148 case 4:
149 img.adfGeoTransform[0] = d;
150 break;
151 case 5:
152 img.adfGeoTransform[3] = d;
153 break;
154 }
155
156 }
157 if (i == 6)
158 hasGeo = true;
159 }
160 }
161 if(!hasGeo)
162 if ( poDataset->GetGeoTransform( img.adfGeoTransform ) != CE_None ) {
163 GDALClose((GDALDatasetH)poDataset);
164 return false;
165 }
166
167 qDebug( "Origin = (%.6f,%.6f)\n",
168 img.adfGeoTransform[0], img.adfGeoTransform[3] );
169
170 qDebug( "Pixel Size = (%.6f,%.6f)\n",
171 img.adfGeoTransform[1], img.adfGeoTransform[5] );
172
173 bbox.setTopLeft(QPointF(img.adfGeoTransform[0], img.adfGeoTransform[3]));
174 bbox.setWidth(img.adfGeoTransform[1]*poDataset->GetRasterXSize());
175 bbox.setHeight(img.adfGeoTransform[5]*poDataset->GetRasterYSize());
176
177 isLatLon = false;
178 if( strlen(poDataset->GetProjectionRef()) != 0 ) {
179 qDebug( "Projection is `%s'\n", poDataset->GetProjectionRef() );
180 OGRSpatialReference* theSrs = new OGRSpatialReference(poDataset->GetProjectionRef());
181 if (theSrs && theSrs->Validate() == OGRERR_NONE) {
182 theSrs->morphFromESRI();
183 char* theProj4;
184 if (theSrs->exportToProj4(&theProj4) == OGRERR_NONE) {
185 qDebug() << "GDAL: to proj4 : " << theProj4;
186 } else {
187 qDebug() << "GDAL: to proj4 error: " << CPLGetLastErrorMsg();
188 GDALClose((GDALDatasetH)poDataset);
189 return false;
190 }
191 QString srsProj = QString(theProj4);
192 if (!srsProj.isEmpty() && theProjection != srsProj) {
193 cleanup();
194 theProjection = srsProj;
195 }
196 isLatLon = (theSrs->IsGeographic() == TRUE);
197 }
198 }
199 if (theProjection.isEmpty()) {
200 theProjection = ProjectionChooser::getProjection(QCoreApplication::translate("ImportExportGdal", "Unable to set projection; please specify one"));
201 if (theProjection.isEmpty()) {
202 GDALClose((GDALDatasetH)poDataset);
203 return false;
204 }
205 }
206
207 qDebug( "Driver: %s/%s\n",
208 poDataset->GetDriver()->GetDescription(),
209 poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME ) );
210
211 qDebug( "Size is %dx%dx%d\n",
212 poDataset->GetRasterXSize(), poDataset->GetRasterYSize(),
213 poDataset->GetRasterCount() );
214
215 GdalAdapter::ImgType theType = GdalAdapter::Unknown;
216 int bandCount = poDataset->GetRasterCount();
217 int ixA = -1;
218 int ixR, ixG, ixB;
219 int ixH, ixS, ixL;
220 int ixC, ixM, ixY, ixK;
221 int ixYuvY, ixYuvU, ixYuvV;
222 double adfMinMax[2];
223 double UnknownUnit;
224 GDALColorTable* colTable = NULL;
225 for (int i=0; i<bandCount; ++i) {
226 GDALRasterBand *poBand = poDataset->GetRasterBand( i+1 );
227 GDALColorInterp bandtype = poBand->GetColorInterpretation();
228 qDebug() << "Band " << i+1 << " Color: " << GDALGetColorInterpretationName(poBand->GetColorInterpretation());
229
230 switch (bandtype)
231 {
232 case GCI_Undefined:
233 theType = GdalAdapter::Unknown;
234 int bGotMin, bGotMax;
235 adfMinMax[0] = poBand->GetMinimum( &bGotMin );
236 adfMinMax[1] = poBand->GetMaximum( &bGotMax );
237 if( ! (bGotMin && bGotMax) )
238 GDALComputeRasterMinMax((GDALRasterBandH)poBand, TRUE, adfMinMax);
239 UnknownUnit = (adfMinMax[1] - adfMinMax[0]) / 256;
240 break;
241 case GCI_GrayIndex:
242 theType = GdalAdapter::GrayScale;
243 break;
244 case GCI_RedBand:
245 theType = GdalAdapter::Rgb;
246 ixR = i;
247 break;
248 case GCI_GreenBand:
249 theType = GdalAdapter::Rgb;
250 ixG = i;
251 break;
252 case GCI_BlueBand :
253 theType = GdalAdapter::Rgb;
254 ixB = i;
255 break;
256 case GCI_HueBand:
257 theType = GdalAdapter::Hsl;
258 ixH = i;
259 break;
260 case GCI_SaturationBand:
261 theType = GdalAdapter::Hsl;
262 ixS = i;
263 break;
264 case GCI_LightnessBand:
265 theType = GdalAdapter::Hsl;
266 ixL = i;
267 break;
268 case GCI_CyanBand:
269 theType = GdalAdapter::Cmyk;
270 ixC = i;
271 break;
272 case GCI_MagentaBand:
273 theType = GdalAdapter::Cmyk;
274 ixM = i;
275 break;
276 case GCI_YellowBand:
277 theType = GdalAdapter::Cmyk;
278 ixY = i;
279 break;
280 case GCI_BlackBand:
281 theType = GdalAdapter::Cmyk;
282 ixK = i;
283 break;
284 case GCI_YCbCr_YBand:
285 theType = GdalAdapter::YUV;
286 ixYuvY = i;
287 break;
288 case GCI_YCbCr_CbBand:
289 theType = GdalAdapter::YUV;
290 ixYuvU = i;
291 break;
292 case GCI_YCbCr_CrBand:
293 theType = GdalAdapter::YUV;
294 ixYuvV = i;
295 break;
296 case GCI_AlphaBand:
297 ixA = i;
298 break;
299 case GCI_PaletteIndex:
300 colTable = poBand->GetColorTable();
301 switch (colTable->GetPaletteInterpretation())
302 {
303 case GPI_Gray :
304 theType = GdalAdapter::Palette_Gray;
305 break;
306 case GPI_RGB :
307 theType = GdalAdapter::Palette_RGBA;
308 break;
309 case GPI_CMYK :
310 theType = GdalAdapter::Palette_CMYK;
311 break;
312 case GPI_HLS :
313 theType = GdalAdapter::Palette_HLS;
314 break;
315 }
316 break;
317 }
318 }
319
320 QSize theImgSize(poDataset->GetRasterXSize(), poDataset->GetRasterYSize());
321 QImage theImg = QImage(theImgSize, QImage::Format_ARGB32);
322
323 // Make sure that lineBuf holds one whole line of data.
324 float *lineBuf;
325 lineBuf = (float *) CPLMalloc(theImgSize.width() * bandCount * sizeof(float));
326
327 int px, py;
328 //every row loop
329 for (int row = 0; row < theImgSize.height(); row++) {
330 py = row;
331 CPLErr err = poDataset->RasterIO( GF_Read, 0, row, theImgSize.width(),
332 1, lineBuf, theImgSize.width(), 1, GDT_Float32, bandCount,
333 NULL, sizeof(float) * bandCount, 0, sizeof(float) );
334 /* FIXME: Perhaps break, or check if more work needs to be done
335 * (filling with an error pattern? */
336 if (err != CE_None) {
337 qDebug() << "RasterIO failed to read row. Skipping.";
338 continue;
339 }
340
341 // every pixel in row.
342 for (int col = 0; col < theImgSize.width(); col++){
343 px = col;
344 switch (theType)
345 {
346 case GdalAdapter::Unknown:
347 {
348 float* v = lineBuf + (col*bandCount);
349 float val = (*v - adfMinMax[0]) / UnknownUnit;
350 theImg.setPixel(px, py, qRgb(val, val, val));
351 break;
352 }
353 case GdalAdapter::GrayScale:
354 {
355 float* v = lineBuf + (col*bandCount);
356 theImg.setPixel(px, py, qRgb(*v, *v, *v));
357 break;
358 }
359 case GdalAdapter::Rgb:
360 {
361 float* r = lineBuf + (col*bandCount) + ixR;
362 float* g = lineBuf + (col*bandCount) + ixG;
363 float* b = lineBuf + (col*bandCount) + ixB;
364 int a = 255;
365 if (ixA != -1) {
366 float* fa = lineBuf + (col*bandCount) + ixA;
367 a = *fa;
368 }
369 theImg.setPixel(px, py, qRgba(*r, *g, *b, a));
370 break;
371 }
372 #if QT_VERSION >= 0x040600
373 case GdalAdapter::Hsl:
374 {
375 float* h = lineBuf + (col*bandCount) + ixH;
376 float* s = lineBuf + (col*bandCount) + ixS;
377 float* l = lineBuf + (col*bandCount) + ixL;
378 int a = 255;
379 if (ixA != -1) {
380 float* fa = lineBuf + (col*bandCount) + ixA;
381 a = *fa;
382 }
383 QColor C = QColor::fromHsl(*h, *s, *l, a);
384 theImg.setPixel(px, py, C.rgba());
385 break;
386 }
387 #endif
388 case GdalAdapter::Cmyk:
389 {
390 float* c = lineBuf + (col*bandCount) + ixC;
391 float* m = lineBuf + (col*bandCount) + ixM;
392 float* y = lineBuf + (col*bandCount) + ixY;
393 float* k = lineBuf + (col*bandCount) + ixK;
394 int a = 255;
395 if (ixA != -1) {
396 float* fa = lineBuf + (col*bandCount) + ixA;
397 a = *fa;
398 }
399 QColor C = QColor::fromCmyk(*c, *m, *y, *k, a);
400 theImg.setPixel(px, py, C.rgba());
401 break;
402 }
403 case GdalAdapter::YUV:
404 {
405 // From http://www.fourcc.org/fccyvrgb.php
406 float* y = lineBuf + (col*bandCount) + ixYuvY;
407 float* u = lineBuf + (col*bandCount) + ixYuvU;
408 float* v = lineBuf + (col*bandCount) + ixYuvV;
409 int a = 255;
410 if (ixA != -1) {
411 float* fa = lineBuf + (col*bandCount) + ixA;
412 a = *fa;
413 }
414 float R = 1.164*(*y - 16) + 1.596*(*v - 128);
415 float G = 1.164*(*y - 16) - 0.813*(*v - 128) - 0.391*(*u - 128);
416 float B = 1.164*(*y - 16) + 2.018*(*u - 128);
417
418 theImg.setPixel(px, py, qRgba(R, G, B, a));
419 break;
420 }
421 case GdalAdapter::Palette_Gray:
422 {
423 float* ix = (lineBuf + (col*bandCount));
424 const GDALColorEntry* color = colTable->GetColorEntry(*ix);
425 theImg.setPixel(px, py, qRgb(color->c1, color->c1, color->c1));
426 break;
427 }
428 case GdalAdapter::Palette_RGBA:
429 {
430 float* ix = (lineBuf + (col*bandCount));
431 const GDALColorEntry* color = colTable->GetColorEntry(*ix);
432 theImg.setPixel(px, py, qRgba(color->c1, color->c2, color->c3, color->c4));
433 break;
434 }
435 #if QT_VERSION >= 0x040600
436 case GdalAdapter::Palette_HLS:
437 {
438 float* ix = (lineBuf + (col*bandCount));
439 const GDALColorEntry* color = colTable->GetColorEntry(*ix);
440 QColor C = QColor::fromHsl(color->c1, color->c2, color->c3, color->c4);
441 theImg.setPixel(px, py, C.rgba());
442 break;
443 }
444 #endif
445 case GdalAdapter::Palette_CMYK:
446 {
447 float* ix = (lineBuf + (col*bandCount));
448 const GDALColorEntry* color = colTable->GetColorEntry(*ix);
449 QColor C = QColor::fromCmyk(color->c1, color->c2, color->c3, color->c4);
450 theImg.setPixel(px, py, C.rgba());
451 break;
452 }
453 }
454 }
455 QCoreApplication::processEvents();
456 }
457
458 img.theFilename = fn;
459 img.theImg = QPixmap::fromImage(theImg);
460 theImages.push_back(img);
461 theBbox = theBbox.united(bbox);
462
463 GDALClose((GDALDatasetH)poDataset);
464 return true;
465 }
466
onLoadImage()467 void GdalAdapter::onLoadImage()
468 {
469 int fileOk = 0;
470
471 QStringList fileNames = QFileDialog::getOpenFileNames(
472 NULL,
473 tr("Open GDAL files"),
474 "", FILTER_OPEN_SUPPORTED);
475 if (fileNames.isEmpty())
476 return;
477
478 // theBbox = QRectF();
479 // theImages.clear();
480
481 for (int i=0; i<fileNames.size(); i++) {
482 if (loadImage(fileNames[i]))
483 ++fileOk;
484 }
485
486 if (!fileOk) {
487 QMessageBox::critical(0,QCoreApplication::translate("GdalBackground","No valid file"),QCoreApplication::translate("GdalBackground","No valid GDAL file could be found."));
488 } else {
489 emit forceZoom();
490 emit forceRefresh();
491 }
492
493 return;
494 }
495
onSetSourceTag()496 void GdalAdapter::onSetSourceTag()
497 {
498 bool ok;
499 QString text = QInputDialog::getText(0, tr("Please specify automatic \"source\" tag value"),
500 tr("Value:"), QLineEdit::Normal, theSourceTag, &ok);
501 if (ok)
502 theSourceTag = text;
503 }
504
getSourceTag() const505 QString GdalAdapter::getSourceTag() const
506 {
507 return theSourceTag;
508 }
509
getHost() const510 QString GdalAdapter::getHost() const
511 {
512 return QString();
513 }
514
getType() const515 IMapAdapter::Type GdalAdapter::getType() const
516 {
517 return IMapAdapter::DirectBackground;
518 }
519
getMenu() const520 QMenu* GdalAdapter::getMenu() const
521 {
522 return theMenu;
523 }
524
getBoundingbox() const525 QRectF GdalAdapter::getBoundingbox() const
526 {
527 QRectF projBbox = theBbox;
528 if (isLatLon)
529 projBbox = QRectF(angToRad(theBbox.left()), angToRad(theBbox.top()), angToRad(theBbox.width()), angToRad(theBbox.height()));
530 return projBbox;
531 }
532
projection() const533 QString GdalAdapter::projection() const
534 {
535 return theProjection;
536 }
537
getPixmap(const QRectF &,const QRectF & theProjBbox,const QRect & src) const538 QPixmap GdalAdapter::getPixmap(const QRectF& /*wgs84Bbox*/, const QRectF& theProjBbox, const QRect& src) const
539 {
540 QPixmap pix(src.size());
541 pix.fill(Qt::transparent);
542 QPainter p(&pix);
543
544 QRectF projBbox = theProjBbox;
545 if (isLatLon)
546 projBbox = QRectF(radToAng(theProjBbox.left()), radToAng(theProjBbox.top()), radToAng(theProjBbox.width()), radToAng(theProjBbox.height()));
547
548 for (int i=0; i<theImages.size(); ++i) {
549 QPixmap theImg = theImages[i].theImg;
550
551 QSizeF sz(projBbox.width() / theImages[i].adfGeoTransform[1], projBbox.height() / theImages[i].adfGeoTransform[5]);
552 if (sz.isNull())
553 return QPixmap();
554
555 QPointF s((projBbox.left() - theImages[i].adfGeoTransform[0]) / theImages[i].adfGeoTransform[1],
556 (projBbox.top() - theImages[i].adfGeoTransform[3]) / theImages[i].adfGeoTransform[5]);
557
558 qDebug() << "Pixmap Origin: " << s.x() << "," << s.y();
559 qDebug() << "Pixmap size: " << sz.width() << "," << sz.height();
560
561 double rtx = src.width() / (double)sz.width();
562 double rty = src.height() / (double)sz.height();
563
564 QRect mRect = QRect(s.toPoint(), sz.toSize());
565 QRect iRect = theImg.rect().intersected(mRect);
566 QRect sRect = QRect(iRect.topLeft() - mRect.topLeft(), iRect.size());
567 QRect fRect = QRect(sRect.x() * rtx, sRect.y() * rty, sRect.width() * rtx, sRect.height() * rty);
568
569 qDebug() << "mrect: " << mRect;
570 qDebug() << "iRect: " << iRect;
571 qDebug() << "sRect: " << sRect;
572
573 // QImage img2 = theImg.copy(iRect).scaled(fRect.size());
574 // p.drawImage(fRect.topLeft(), img2);
575 QPixmap img2 = theImg.copy(iRect).scaled(fRect.size());
576 p.drawPixmap(fRect.topLeft(), img2);
577 }
578
579 p.end();
580 return pix;
581 }
582
getImageManager()583 IImageManager* GdalAdapter::getImageManager()
584 {
585 return NULL;
586 }
587
setImageManager(IImageManager *)588 void GdalAdapter::setImageManager(IImageManager* /*anImageManager*/)
589 {
590 }
591
cleanup()592 void GdalAdapter::cleanup()
593 {
594 theImages.clear();
595 theBbox = QRectF();
596 theProjection = QString();
597 }
598
toXML(QXmlStreamWriter & stream)599 bool GdalAdapter::toXML(QXmlStreamWriter& stream)
600 {
601 bool OK = true;
602
603 stream.writeStartElement("Images");
604
605 stream.writeAttribute("projection", theProjection);
606 if (!theSourceTag.isEmpty())
607 stream.writeAttribute("source", theSourceTag);
608 for (int i=0; i<theImages.size(); ++i) {
609 stream.writeStartElement("Image");
610 stream.writeAttribute("filename", theImages[i].theFilename);
611 stream.writeEndElement();
612 }
613
614 stream.writeEndElement();
615
616 return OK;
617 }
618
fromXML(QXmlStreamReader & stream)619 void GdalAdapter::fromXML(QXmlStreamReader& stream)
620 {
621 theBbox = QRectF();
622 theImages.clear();
623
624 while(!stream.atEnd() && !stream.isEndElement()) {
625 if (stream.name() == "Images") {
626 if (stream.attributes().hasAttribute("projection"))
627 theProjection = stream.attributes().value("projection").toString();
628 if (stream.attributes().hasAttribute("source"))
629 theSourceTag = stream.attributes().value("source").toString();
630 stream.readNext();
631 while(!stream.atEnd() && !stream.isEndElement()) {
632 if (stream.name() == "Image") {
633 QString fn = stream.attributes().value("filename").toString();
634 if (!fn.isEmpty())
635 loadImage(fn);
636 stream.readNext();
637 } else if (!stream.isWhitespace()) {
638 qDebug() << "gdalimage: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
639 stream.skipCurrentElement();
640 }
641 stream.readNext();
642 }
643 } else if (!stream.isWhitespace()) {
644 qDebug() << "gdalmain: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
645 stream.skipCurrentElement();
646 }
647
648 stream.readNext();
649 }
650 }
651
toPropertiesHtml()652 QString GdalAdapter::toPropertiesHtml()
653 {
654 QString h;
655
656 QStringList fn;
657 for (int i=0; i<theImages.size(); ++i) {
658 fn << QDir::toNativeSeparators(theImages[i].theFilename);
659 }
660 h += "<i>" + tr("Filename(s)") + ": </i>" + fn.join("; ");
661
662 return h;
663 }
664
665 #if !(QT_VERSION >= QT_VERSION_CHECK(5,0,0))
666 Q_EXPORT_PLUGIN2(MGdalBackgroundPlugin, GdalAdapterFactory)
667 #endif
668