1 //***************************************************************
2 // CLass: GeoTiffAdapter
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 "GeoTiffAdapter.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 ("{867e78e9-3156-45f8-a9a7-e5cfa52f8507}");
33 static const QString theName("GeoTIFF");
34 
getId() const35 QUuid GeoTiffAdapterFactory::getId() const
36 {
37     return theUid;
38 }
39 
getName() const40 QString	GeoTiffAdapterFactory::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("Supported formats")+" (*.tif *.tiff)\n" \
59     +tr("GeoTIFF files (*.tif *.tiff)\n") \
60     +tr("All Files (*)")
61 
GeoTiffAdapter()62 GeoTiffAdapter::GeoTiffAdapter()
63     : poDataset(0), isLatLon(false)
64 {
65     GDALAllRegister();
66 
67     QAction* loadImage = new QAction(tr("Load image(s)..."), this);
68     loadImage->setData(theUid.toString());
69     connect(loadImage, SIGNAL(triggered()), SLOT(onLoadImage()));
70     QAction* setSource = new QAction(tr("Specify \"source\" tag..."), this);
71     setSource->setData(theUid.toString());
72     connect(setSource, SIGNAL(triggered()), SLOT(onSetSourceTag()));
73 
74     theMenu = new QMenu();
75     theMenu->addAction(loadImage);
76     theMenu->addAction(setSource);
77 }
78 
79 
~GeoTiffAdapter()80 GeoTiffAdapter::~GeoTiffAdapter()
81 {
82     cleanup();
83 }
84 
getId() const85 QUuid GeoTiffAdapter::getId() const
86 {
87     return theUid;
88 }
89 
getName() const90 QString	GeoTiffAdapter::getName() const
91 {
92     return theName;
93 }
94 
alreadyLoaded(QString fn) const95 bool GeoTiffAdapter::alreadyLoaded(QString fn) const
96 {
97     for (int j=0; j<theImages.size(); ++j)
98         if (theImages[j].theFilename == fn)
99             return true;
100     return false;
101 }
102 
loadImage(const QString & fn)103 bool GeoTiffAdapter::loadImage(const QString& fn)
104 {
105     if (alreadyLoaded(fn))
106         return true;
107 
108     QFileInfo fi(fn);
109     GdalImage img;
110     QRectF bbox;
111 
112     poDataset = (GDALDataset *) GDALOpen( QDir::toNativeSeparators(fi.absoluteFilePath()).toUtf8().constData(), GA_ReadOnly );
113     if( poDataset == NULL )
114     {
115         qDebug() <<  "GDAL Open failed: " << fn;
116         return false;
117     }
118 
119     bool hasGeo = false;
120     QDir dir(fi.absoluteDir());
121     QString f = fi.baseName();
122     QStringList wldFilter;
123     wldFilter <<  f+".tfw" << f+".tifw" << f+".tiffw" << f+".wld";
124     QFileInfoList fil = dir.entryInfoList(wldFilter);
125     if (fil.count()) {
126         QFile wld(fil[0].absoluteFilePath());
127         if (wld.open(QIODevice::ReadOnly)) {
128             int i;
129             for (i=0; i<6; ++i) {
130                 if (wld.atEnd())
131                     break;
132                 QString l = wld.readLine();
133                 bool ok;
134                 double d = l.toDouble(&ok);
135                 if (!ok)
136                     break;
137                 switch (i) {
138                 case 0:
139                     img.adfGeoTransform[1] = d;
140                     break;
141                 case 1:
142                     img.adfGeoTransform[4] = d;
143                     break;
144                 case 2:
145                     img.adfGeoTransform[2] = d;
146                     break;
147                 case 3:
148                     img.adfGeoTransform[5] = d;
149                     break;
150                 case 4:
151                     img.adfGeoTransform[0] = d;
152                     break;
153                 case 5:
154                     img.adfGeoTransform[3] = d;
155                     break;
156                 }
157 
158             }
159             if (i == 6)
160                 hasGeo = true;
161         }
162     }
163     if(!hasGeo)
164         if ( poDataset->GetGeoTransform( img.adfGeoTransform ) != CE_None ) {
165             GDALClose((GDALDatasetH)poDataset);
166             return false;
167         }
168 
169     qDebug( "Origin = (%.6f,%.6f)\n",
170             img.adfGeoTransform[0], img.adfGeoTransform[3] );
171 
172     qDebug( "Pixel Size = (%.6f,%.6f)\n",
173             img.adfGeoTransform[1], img.adfGeoTransform[5] );
174 
175     bbox.setTopLeft(QPointF(img.adfGeoTransform[0], img.adfGeoTransform[3]));
176     bbox.setWidth(img.adfGeoTransform[1]*poDataset->GetRasterXSize());
177     bbox.setHeight(img.adfGeoTransform[5]*poDataset->GetRasterYSize());
178 
179     isLatLon = false;
180     if( strlen(poDataset->GetProjectionRef()) != 0 ) {
181         qDebug( "Projection is `%s'\n", poDataset->GetProjectionRef() );
182         OGRSpatialReference* theSrs = new OGRSpatialReference(poDataset->GetProjectionRef());
183         if (theSrs && theSrs->Validate() == OGRERR_NONE) {
184             theSrs->morphFromESRI();
185             char* theProj4;
186             if (theSrs->exportToProj4(&theProj4) == OGRERR_NONE) {
187                 qDebug() << "GDAL: to proj4 : " << theProj4;
188             } else {
189                 qDebug() << "GDAL: to proj4 error: " << CPLGetLastErrorMsg();
190                 GDALClose((GDALDatasetH)poDataset);
191                 return false;
192             }
193             QString srsProj = QString(theProj4);
194             if (!srsProj.isEmpty() && theProjection != srsProj) {
195                 cleanup();
196                 theProjection = srsProj;
197             }
198             isLatLon = (theSrs->IsGeographic() == TRUE);
199         }
200     }
201     if (theProjection.isEmpty()) {
202         theProjection = ProjectionChooser::getProjection(QCoreApplication::translate("ImportExportGdal", "Unable to set projection; please specify one"));
203         if (theProjection.isEmpty()) {
204             GDALClose((GDALDatasetH)poDataset);
205             return false;
206         }
207     }
208 
209     qDebug( "Driver: %s/%s\n",
210             poDataset->GetDriver()->GetDescription(),
211             poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME ) );
212 
213     qDebug( "Size is %dx%dx%d\n",
214             poDataset->GetRasterXSize(), poDataset->GetRasterYSize(),
215             poDataset->GetRasterCount() );
216 
217     img.theFilename = fn;
218     img.theImg.load(fn);
219     theImages.push_back(img);
220     theBbox = theBbox.united(bbox);
221 
222     GDALClose((GDALDatasetH)poDataset);
223     return true;
224 }
225 
onLoadImage()226 void GeoTiffAdapter::onLoadImage()
227 {
228     int fileOk = 0;
229 
230     QStringList fileNames = QFileDialog::getOpenFileNames(
231                     NULL,
232                     tr("Open GeoTIFF files"),
233                     "", FILTER_OPEN_SUPPORTED);
234     if (fileNames.isEmpty())
235         return;
236 
237 //    theBbox = QRectF();
238 //    theImages.clear();
239 
240     for (int i=0; i<fileNames.size(); i++) {
241         if (loadImage(fileNames[i]))
242             ++fileOk;
243     }
244 
245     if (!fileOk) {
246         QMessageBox::critical(0,QCoreApplication::translate("GeoTiffBackground","No valid file"),QCoreApplication::translate("GeoTiffBackground","No valid GeoTIFF file could be found."));
247     } else {
248         emit(forceProjection());
249         emit forceZoom();
250         emit forceRefresh();
251     }
252 
253     return;
254 }
255 
onSetSourceTag()256 void GeoTiffAdapter::onSetSourceTag()
257 {
258     bool ok;
259     QString text = QInputDialog::getText(0, tr("Please specify automatic \"source\" tag value"),
260                                          tr("Value:"), QLineEdit::Normal, theSourceTag, &ok);
261     if (ok)
262         theSourceTag = text;
263 }
264 
getSourceTag() const265 QString GeoTiffAdapter::getSourceTag() const
266 {
267     return theSourceTag;
268 }
269 
getHost() const270 QString	GeoTiffAdapter::getHost() const
271 {
272     return QString();
273 }
274 
getType() const275 IMapAdapter::Type GeoTiffAdapter::getType() const
276 {
277     return IMapAdapter::DirectBackground;
278 }
279 
getMenu() const280 QMenu* GeoTiffAdapter::getMenu() const
281 {
282     return theMenu;
283 }
284 
getBoundingbox() const285 QRectF GeoTiffAdapter::getBoundingbox() const
286 {
287     QRectF projBbox = theBbox;
288     if (isLatLon)
289         projBbox = QRectF(angToRad(theBbox.left()), angToRad(theBbox.top()), angToRad(theBbox.width()), angToRad(theBbox.height()));
290     return projBbox;
291 }
292 
projection() const293 QString GeoTiffAdapter::projection() const
294 {
295     return theProjection;
296 }
297 
getPixmap(const QRectF &,const QRectF & theProjBbox,const QRect & src) const298 QPixmap GeoTiffAdapter::getPixmap(const QRectF& /*wgs84Bbox*/, const QRectF& theProjBbox, const QRect& src) const
299 {
300     QPixmap pix(src.size());
301     pix.fill(Qt::transparent);
302     QPainter p(&pix);
303 
304     QRectF projBbox = theProjBbox;
305     if (isLatLon)
306         projBbox = QRectF(radToAng(theProjBbox.left()), radToAng(theProjBbox.top()), radToAng(theProjBbox.width()), radToAng(theProjBbox.height()));
307 
308 
309     for (int i=0; i<theImages.size(); ++i) {
310         QPixmap theImg = theImages[i].theImg;
311 
312         QSizeF sz(projBbox.width() / theImages[i].adfGeoTransform[1], projBbox.height() / theImages[i].adfGeoTransform[5]);
313         if (sz.isNull())
314             return QPixmap();
315 
316         QPointF s((projBbox.left() - theImages[i].adfGeoTransform[0]) / theImages[i].adfGeoTransform[1],
317                  (projBbox.top() - theImages[i].adfGeoTransform[3]) / theImages[i].adfGeoTransform[5]);
318 
319         qDebug() << "Pixmap Origin: " << s.x() << "," << s.y();
320         qDebug() << "Pixmap size: " << sz.width() << "," << sz.height();
321 
322         double rtx = src.width() / (double)sz.width();
323         double rty = src.height() / (double)sz.height();
324 
325         QRect mRect = QRect(s.toPoint(), sz.toSize());
326         QRect iRect = theImg.rect().intersected(mRect);
327         QRect sRect = QRect(iRect.topLeft() - mRect.topLeft(), iRect.size());
328         QRect fRect = QRect(sRect.x() * rtx, sRect.y() * rty, sRect.width() * rtx, sRect.height() * rty);
329 
330         qDebug() << "mrect: " << mRect;
331         qDebug() << "iRect: " << iRect;
332         qDebug() << "sRect: " << sRect;
333 
334     //	QImage img2 = theImg.copy(iRect).scaled(fRect.size());
335     //	p.drawImage(fRect.topLeft(), img2);
336         QPixmap img2 = theImg.copy(iRect).scaled(fRect.size());
337         p.drawPixmap(fRect.topLeft(), img2);
338     }
339 
340     p.end();
341     return pix;
342 }
343 
getImageManager()344 IImageManager* GeoTiffAdapter::getImageManager()
345 {
346     return NULL;
347 }
348 
setImageManager(IImageManager *)349 void GeoTiffAdapter::setImageManager(IImageManager* /*anImageManager*/)
350 {
351 }
352 
cleanup()353 void GeoTiffAdapter::cleanup()
354 {
355     theImages.clear();
356     theBbox = QRectF();
357     theProjection = QString();
358 }
359 
toXML(QXmlStreamWriter & stream)360 bool GeoTiffAdapter::toXML(QXmlStreamWriter& stream)
361 {
362     bool OK = true;
363 
364     stream.writeStartElement("Images");
365 
366     stream.writeAttribute("projection", theProjection);
367     if (!theSourceTag.isEmpty())
368         stream.writeAttribute("source", theSourceTag);
369     for (int i=0; i<theImages.size(); ++i) {
370         stream.writeStartElement("Image");
371         stream.writeAttribute("filename", theImages[i].theFilename);
372         stream.writeEndElement();
373     }
374 
375     stream.writeEndElement();
376 
377     return OK;
378 }
379 
fromXML(QXmlStreamReader & stream)380 void GeoTiffAdapter::fromXML(QXmlStreamReader& stream)
381 {
382     theBbox = QRectF();
383     theImages.clear();
384 
385     while(!stream.atEnd() && !stream.isEndElement()) {
386         if (stream.name() == "Images") {
387             if (stream.attributes().hasAttribute("projection"))
388                 theProjection = stream.attributes().value("projection").toString();
389             if (stream.attributes().hasAttribute("source"))
390                 theSourceTag = stream.attributes().value("source").toString();
391             stream.readNext();
392             while(!stream.atEnd() && !stream.isEndElement()) {
393                 if (stream.name() == "Image") {
394                     QString fn = stream.attributes().value("filename").toString();
395                     if (!fn.isEmpty())
396                         loadImage(fn);
397                     stream.readNext();
398                 } else if (!stream.isWhitespace()) {
399                     qDebug() << "gdalimage: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
400                     stream.skipCurrentElement();
401                 }
402                 stream.readNext();
403             }
404         } else if (!stream.isWhitespace()) {
405             qDebug() << "gdalmain: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
406             stream.skipCurrentElement();
407         }
408 
409         stream.readNext();
410     }
411 }
412 
toPropertiesHtml()413 QString GeoTiffAdapter::toPropertiesHtml()
414 {
415     QString h;
416 
417     QStringList fn;
418     for (int i=0; i<theImages.size(); ++i) {
419         fn << QDir::toNativeSeparators(theImages[i].theFilename);
420     }
421     h += "<i>" + tr("Filename(s)") + ": </i>" + fn.join("; ");
422 
423     return h;
424 }
425 
426 #if !(QT_VERSION >= QT_VERSION_CHECK(5,0,0))
427 Q_EXPORT_PLUGIN2(MGeoTiffBackgroundPlugin, GeoTiffAdapterFactory)
428 #endif
429 
430