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 "WalkingPapersAdapter.h"
14
15 #include <QCoreApplication>
16 #include <QtPlugin>
17 #include <QAction>
18 #include <QFileDialog>
19 #include <QPainter>
20 #include <QMessageBox>
21 #include <QInputDialog>
22 #include <QTimer>
23 #include <QImage>
24
25 #include <QNetworkAccessManager>
26 #include <QNetworkRequest>
27 #include <QNetworkReply>
28
29 #include <QDebug>
30
31 #include <math.h>
32
33 #ifdef USE_ZBAR
34 #include <zbar.h>
35 #include <zbar/QZBarImage.h>
36 #endif
37
38 // from wikipedia
39 #define EQUATORIALRADIUS 6378137.0
40 #define POLARRADIUS 6356752.0
41 #define EQUATORIALMETERCIRCUMFERENCE 40075016.68
42 #define EQUATORIALMETERHALFCIRCUMFERENCE 20037508.34
43 #define EQUATORIALMETERPERDEGREE 222638.981555556
44
45 static const QUuid theUid ("{c580b2bc-dd14-40b2-8bb6-241da2a1fdb3}");
46 static const QString theName("Walking Papers");
47
getId() const48 QUuid WalkingPapersAdapterFactory::getId() const
49 {
50 return theUid;
51 }
52
getName() const53 QString WalkingPapersAdapterFactory::getName() const
54 {
55 return theName;
56 }
57
58 /**************/
59
60
61 #define FILTER_OPEN_SUPPORTED \
62 tr("Supported formats")+" (*.jpg *.png *.bmp)\n" \
63 +tr("All Files (*)")
64
angToRad(double a)65 double angToRad(double a)
66 {
67 return a*M_PI/180.;
68 }
69
mercatorProject(const QPointF & c)70 QPointF mercatorProject(const QPointF& c)
71 {
72 double x = angToRad(c.x()) / M_PI * EQUATORIALMETERHALFCIRCUMFERENCE;
73 double y = log(tan(angToRad(c.y())) + 1/cos(angToRad(c.y()))) / M_PI * (EQUATORIALMETERHALFCIRCUMFERENCE);
74
75 return QPointF(x, y);
76 }
77
WalkingPapersAdapter()78 WalkingPapersAdapter::WalkingPapersAdapter()
79 : theImageManager(0)
80 {
81 QAction* loadImage = new QAction(tr("Load image..."), this);
82 loadImage->setData(theUid.toString());
83 connect(loadImage, SIGNAL(triggered()), SLOT(onLoadImage()));
84 theMenu = new QMenu();
85 theMenu->addAction(loadImage);
86 }
87
88
~WalkingPapersAdapter()89 WalkingPapersAdapter::~WalkingPapersAdapter()
90 {
91 }
92
getId() const93 QUuid WalkingPapersAdapter::getId() const
94 {
95 return theUid;
96 }
97
getName() const98 QString WalkingPapersAdapter::getName() const
99 {
100 return theName;
101 }
102
alreadyLoaded(QString fn) const103 bool WalkingPapersAdapter::alreadyLoaded(QString fn) const
104 {
105 for (int j=0; j<theImages.size(); ++j)
106 if (theImages[j].theFilename == fn)
107 return true;
108 return false;
109 }
110
make_grayscale(QImage & in)111 void make_grayscale(QImage& in)
112 {
113 if(in.format()!=QImage::Format_Indexed8)
114 throw "format error";
115 QVector<int> transform_table(in.colorCount());
116 for(int i=0;i<in.colorCount();i++)
117 {
118 QRgb c1=in.color(i);
119 int avg=qGray(c1);
120 transform_table[i] = avg;
121 }
122 in.setColorCount(256);
123 for(int i=0;i<256;i++)
124 in.setColor(i,qRgb(i,i,i));
125 for(int i=0;i<in.byteCount();i++)
126 {
127 in.bits()[i]=transform_table[in.bits()[i]];
128 }
129 }
130
getWalkingPapersDetails(const QUrl & reqUrl,QRectF & bbox) const131 bool WalkingPapersAdapter::getWalkingPapersDetails(const QUrl& reqUrl, QRectF& bbox) const
132 {
133 QNetworkAccessManager* manager = theImageManager->getNetworkManager();
134 QEventLoop q;
135 QTimer tT;
136
137 if (!reqUrl.host().contains("walking-papers.org"))
138 return false;
139
140 tT.setSingleShot(true);
141 connect(&tT, SIGNAL(timeout()), &q, SLOT(quit()));
142 connect(manager, SIGNAL(finished(QNetworkReply*)),
143 &q, SLOT(quit()));
144 QNetworkReply *reply = manager->get(QNetworkRequest(reqUrl));
145
146 tT.start(theSets->value("Network/NetworkTimeout", 5000).toInt());
147 q.exec();
148 if(tT.isActive()) {
149 // download complete
150 tT.stop();
151 } else {
152 QMessageBox::warning(0, tr("Network timeout"), tr("Cannot read the photo's details from the Walking Papers server."), QMessageBox::Ok);
153 return false;
154 }
155
156 QString center = QString::fromLatin1(reply->rawHeader("X-Print-Bounds"));
157 QStringList sl = center.split(" ");
158 if (sl.size() != 4)
159 return false;
160
161 QPointF tl(sl[1].toDouble(), sl[0].toDouble());
162 QPointF br(sl[3].toDouble(), sl[2].toDouble());
163
164 qDebug() << tl << "; " << br;
165
166 bbox = QRectF(tl, br);
167
168 return true;
169 }
170
askAndgetWalkingPapersDetails(QRectF & bbox) const171 bool WalkingPapersAdapter::askAndgetWalkingPapersDetails(QRectF& bbox) const
172 {
173 bool ok;
174 QString text = QInputDialog::getText(0, tr("Please specify Walking Papers URL"),
175 tr("URL:"), QLineEdit::Normal, "", &ok);
176 if (ok && !text.isEmpty()) {
177 QUrl url(text);
178 return getWalkingPapersDetails(url, bbox);
179 } else
180 return false;
181 }
182
loadImage(const QString & fn,QRectF theBbox,int theRotation)183 bool WalkingPapersAdapter::loadImage(const QString& fn, QRectF theBbox, int theRotation)
184 {
185 if (alreadyLoaded(fn))
186 return true;
187
188 QImage img(fn);
189 WalkingPapersImage wimg;
190 if (theBbox.isNull()) {
191 #ifdef USE_ZBAR
192 zbar::QZBarImage image(img);
193
194 // create a reader
195 zbar::ImageScanner scanner;
196
197 // configure the reader
198 scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
199
200 // scan the image for barcodes
201 scanner.recycle_image(image);
202 zbar::Image tmp = image.convert(*(long*)"Y800");
203 int n = scanner.scan(tmp);
204 image.set_symbols(tmp.get_symbols());
205
206 if (n <= 0) {
207 qDebug() << "WP scan error: " << n;
208 if (!askAndgetWalkingPapersDetails(theBbox))
209 return false;
210 } else {
211 QUrl url;
212 // extract results
213 for(zbar::Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol) {
214 // do something useful with results
215 qDebug() << "decoded " << QString::fromStdString(symbol->get_type_name())
216 << " symbol \"" << QString::fromStdString(symbol->get_data()) << '"';
217 qDebug() << "x;y: " << symbol->get_location_x(0) << ", " << symbol->get_location_y(0);
218
219 url = QUrl(QString::fromStdString(symbol->get_data()));
220 getWalkingPapersDetails(url, theBbox);
221
222 int x = symbol->get_location_x(0);
223 int y = symbol->get_location_y(0);
224 QPoint mid = QPoint(img.width()/2, img.height()/2);
225 if (x < mid.x() || y < mid.y()) {
226 if (x < mid.x() && y < mid.y())
227 theRotation = 180;
228 else if (x > mid.x() && y < mid.y())
229 theRotation = 90;
230 else if (x < mid.x() && y > mid.y())
231 theRotation = -90;
232 }
233 }
234 }
235
236 // clean up
237 image.set_data(NULL, 0);
238 #else
239 if (!askAndgetWalkingPapersDetails(theBbox))
240 return false;
241 #endif
242 }
243 if (theRotation) {
244 QMatrix mat;
245 mat.rotate(theRotation);
246 img = img.transformed(mat);
247 }
248 wimg.theFilename = fn;
249 wimg.theImg = QPixmap::fromImage(img);
250 wimg.theBBox = theBbox;
251 wimg.rotation = theRotation;
252 theImages.push_back(wimg);
253
254 theCoordBbox |= theBbox;
255
256 return true;
257 }
258
onLoadImage()259 void WalkingPapersAdapter::onLoadImage()
260 {
261 int fileOk = 0;
262
263 QStringList fileNames = QFileDialog::getOpenFileNames(
264 NULL,
265 tr("Open Walking Papers scan"),
266 "", FILTER_OPEN_SUPPORTED);
267 if (fileNames.isEmpty())
268 return;
269
270 QRectF theBbox = QRectF();
271 for (int i=0; i<fileNames.size(); i++) {
272 if (loadImage(fileNames[i], theBbox))
273 ++fileOk;
274 }
275
276 if (!fileOk) {
277 QMessageBox::critical(0,QCoreApplication::translate("WalkingPapersBackground","No valid file"),QCoreApplication::translate("WalkingPapersBackground","Cannot load file."));
278 } else {
279 emit forceProjection();
280 emit forceZoom();
281 emit forceRefresh();
282 }
283
284 return;
285 }
286
getHost() const287 QString WalkingPapersAdapter::getHost() const
288 {
289 return QString();
290 }
291
getType() const292 IMapAdapter::Type WalkingPapersAdapter::getType() const
293 {
294 return IMapAdapter::DirectBackground;
295 }
296
getMenu() const297 QMenu* WalkingPapersAdapter::getMenu() const
298 {
299 return theMenu;
300 }
301
getBoundingbox() const302 QRectF WalkingPapersAdapter::getBoundingbox() const
303 {
304 QRectF projBBox;
305 projBBox = QRectF(mercatorProject(theCoordBbox.topLeft()), mercatorProject(theCoordBbox.bottomRight()));
306 return projBBox;
307 }
308
projection() const309 QString WalkingPapersAdapter::projection() const
310 {
311 return "EPSG:900913";
312 }
313
getPixmap(const QRectF & wgs84Bbox,const QRectF &,const QRect & src) const314 QPixmap WalkingPapersAdapter::getPixmap(const QRectF& wgs84Bbox, const QRectF& /*projBbox*/, const QRect& src) const
315 {
316 QPixmap pix(src.size());
317 pix.fill(Qt::transparent);
318 QPainter p(&pix);
319
320 for (int i=0; i<theImages.size(); ++i) {
321 QPixmap theImg = theImages[i].theImg;
322
323 double rx = wgs84Bbox.width() / src.width();
324 double ry = wgs84Bbox.height() / src.height();
325 qDebug() << "rx: " << rx << "; ry: " << ry;
326
327 QSize sz(theImages[i].theBBox.width() / rx, theImages[i].theBBox.height() / ry);
328 if (sz.isNull())
329 return QPixmap();
330
331 QPoint s((theImages[i].theBBox.bottomLeft().x() - wgs84Bbox.bottomLeft().x()) / rx, (wgs84Bbox.bottomLeft().y() - theImages[i].theBBox.bottomLeft().y()) / ry);
332
333 qDebug() << "Viewport: " << wgs84Bbox;
334 qDebug() << "Pixmap Origin: " << s.x() << "," << s.y();
335 qDebug() << "Pixmap size: " << sz.width() << "," << sz.height();
336
337 double rtx = theImg.width() / (double)sz.width();
338 double rty = theImg.height() / (double)sz.height();
339
340 QRect mRect = QRect(s, sz);
341 QRect iRect = theImg.rect().intersected(mRect);
342 QRect sRect = QRect(iRect.topLeft() - mRect.topLeft(), iRect.size());
343 QRect fRect = QRect(sRect.x() * rtx, sRect.y() * rty, sRect.width() * rtx, sRect.height() * rty);
344
345 qDebug() << "mrect: " << mRect;
346 qDebug() << "iRect: " << iRect;
347 qDebug() << "sRect: " << sRect;
348 qDebug() << "fRect: " << fRect;
349
350 QPixmap img2 = theImg.copy(fRect).scaled(sRect.size());
351 p.drawPixmap(iRect.topLeft(), img2);
352 }
353
354 p.end();
355 return pix;
356 }
357
getImageManager()358 IImageManager* WalkingPapersAdapter::getImageManager()
359 {
360 return theImageManager;
361 }
362
setImageManager(IImageManager * anImageManager)363 void WalkingPapersAdapter::setImageManager(IImageManager* anImageManager)
364 {
365 theImageManager = anImageManager;
366 }
367
cleanup()368 void WalkingPapersAdapter::cleanup()
369 {
370 theImages.clear();
371 theCoordBbox = QRectF();
372 }
373
toXML(QXmlStreamWriter & stream)374 bool WalkingPapersAdapter::toXML(QXmlStreamWriter& stream)
375 {
376 bool OK = true;
377
378 stream.writeStartElement("Images");
379 for (int i=0; i<theImages.size(); ++i) {
380 stream.writeStartElement("Image");
381 stream.writeAttribute("filename", theImages[i].theFilename);
382 stream.writeAttribute("top", QString::number(theImages[i].theBBox.top()));
383 stream.writeAttribute("left", QString::number(theImages[i].theBBox.left()));
384 stream.writeAttribute("width", QString::number(theImages[i].theBBox.width()));
385 stream.writeAttribute("height", QString::number(theImages[i].theBBox.height()));
386 stream.writeAttribute("rotation", QString::number(theImages[i].rotation));
387 stream.writeEndElement();
388 }
389 stream.writeEndElement();
390
391 return OK;
392 }
393
fromXML(QXmlStreamReader & stream)394 void WalkingPapersAdapter::fromXML(QXmlStreamReader& stream)
395 {
396 theCoordBbox = QRectF();
397 theImages.clear();
398
399 stream.readNext();
400 while(!stream.atEnd() && !stream.isEndElement()) {
401 if (stream.name() == "Images") {
402 stream.readNext();
403 while(!stream.atEnd() && !stream.isEndElement()) {
404 if (stream.name() == "Image") {
405 QString fn = stream.attributes().value("filename").toString();
406 if (!fn.isEmpty()) {
407 double x = stream.attributes().value("left").toString().toDouble();
408 double y = stream.attributes().value("top").toString().toDouble();
409 double w = stream.attributes().value("width").toString().toDouble();
410 double h = stream.attributes().value("height").toString().toDouble();
411 int r = stream.attributes().value("rotation").toString().toInt();
412 QRectF bbox(x, y, w, h);
413 loadImage(fn, bbox, r);
414 }
415 stream.readNext();
416 } else if (!stream.isWhitespace()) {
417 qDebug() << "wp: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
418 stream.skipCurrentElement();
419 }
420 stream.readNext();
421 }
422 } else if (!stream.isWhitespace()) {
423 qDebug() << "wp: logic error: " << stream.name() << " : " << stream.tokenType() << " (" << stream.lineNumber() << ")";
424 stream.skipCurrentElement();
425 }
426 stream.readNext();
427 }
428 }
429
toPropertiesHtml()430 QString WalkingPapersAdapter::toPropertiesHtml()
431 {
432 QString h;
433
434 QStringList fn;
435 for (int i=0; i<theImages.size(); ++i) {
436 fn << QDir::toNativeSeparators(theImages[i].theFilename);
437 }
438 h += "<i>" + tr("Filename(s)") + ": </i>" + fn.join("; ");
439
440 return h;
441 }
442
443
444 #if !(QT_VERSION >= QT_VERSION_CHECK(5,0,0))
445 Q_EXPORT_PLUGIN2(MWalkingPapersBackgroundPlugin, WalkingPapersAdapterFactory)
446 #endif
447