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