Added land and see areas labels
[GPXSee.git] / src / map / ozimap.cpp
blobd4206f11aaceee679d827e86556947855baf22f7
1 #include <QPainter>
2 #include <QFileInfo>
3 #include <QMap>
4 #include <QDir>
5 #include <QBuffer>
6 #include <QImageReader>
7 #include <QPixmapCache>
8 #include <QRegularExpression>
9 #include "common/coordinates.h"
10 #include "common/rectc.h"
11 #include "tar.h"
12 #include "ozf.h"
13 #include "image.h"
14 #include "mapfile.h"
15 #include "gmifile.h"
16 #include "rectd.h"
17 #include "ozimap.h"
20 static QString tarFile(const QString &path)
22 QDir dir(path);
23 QFileInfoList files = dir.entryInfoList(QDir::Files);
25 for (int i = 0; i < files.size(); i++) {
26 const QFileInfo &fi = files.at(i);
28 if (fi.suffix().toLower() == "tar")
29 return fi.absoluteFilePath();
32 return QString();
35 QString OziMap::calibrationFile(const QStringList &files, const QString path,
36 CalibrationType &type)
38 for (int i = 0; i < files.size(); i++) {
39 QFileInfo fi(files.at(i));
40 QString suffix(fi.suffix().toLower());
42 if (path.endsWith(fi.path())) {
43 if (suffix == "map") {
44 type = MAP;
45 return files.at(i);
46 } else if (suffix == "gmi") {
47 type = GMI;
48 return files.at(i);
53 type = Unknown;
54 return QString();
57 OziMap::OziMap(const QString &fileName, CalibrationType type,
58 const Projection &proj, QObject *parent) : Map(fileName, parent), _img(0),
59 _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0), _valid(false)
61 // TAR maps
62 if (type == Unknown) {
63 _tar = new Tar(fileName);
64 if (!_tar->open()) {
65 _errorString = _tar->errorString();
66 return;
68 QStringList files(_tar->files());
69 QString cf(calibrationFile(files, ".", type));
71 if (type == GMI) {
72 QByteArray ba(_tar->file(cf));
73 QBuffer buffer(&ba);
74 GmiFile gmi(buffer);
75 if (!gmi.isValid()) {
76 _errorString = gmi.errorString();
77 return;
78 } else {
79 _name = Util::file2name(fileName);
80 _map.size = gmi.size();
81 _map.path = gmi.image();
82 _calibrationPoints = gmi.calibrationPoints();
83 _projection = proj;
84 computeTransform();
86 } else if (type == MAP) {
87 QByteArray ba(_tar->file(cf));
88 QBuffer buffer(&ba);
89 MapFile mf(buffer);
90 if (!mf.isValid()) {
91 _errorString = mf.errorString();
92 return;
93 } else {
94 _name = mf.name();
95 _map.size = mf.size();
96 _map.path = mf.image();
97 _projection = mf.projection();
98 _transform = mf.transform();
100 } else {
101 _errorString = "No calibration file found";
102 return;
105 if (!setTileInfo(files))
106 return;
108 _tar->close();
110 // regular MAP or GMI maps
111 } else {
112 QFile file(fileName);
114 if (type == MAP) {
115 MapFile mf(file);
116 if (!mf.isValid()) {
117 _errorString = mf.errorString();
118 return;
119 } else {
120 _name = mf.name();
121 _map.size = mf.size();
122 _map.path = mf.image();
123 _projection = mf.projection();
124 _transform = mf.transform();
126 } else if (type == GMI) {
127 GmiFile gmi(file);
128 if (!gmi.isValid()) {
129 _errorString = gmi.errorString();
130 return;
131 } else {
132 _name = Util::file2name(fileName);
133 _map.size = gmi.size();
134 _map.path = gmi.image();
135 _calibrationPoints = gmi.calibrationPoints();
136 _projection = proj;
137 computeTransform();
141 QFileInfo fi(fileName);
142 QDir set(fi.absolutePath() + "/" + "set");
143 if (set.exists()) {
144 if (!setTileInfo(set.entryList(), set.absolutePath()))
145 return;
146 } else {
147 if (!setImageInfo(fi.absolutePath()))
148 return;
152 _valid = true;
155 OziMap::OziMap(const QString &dirName, Tar &tar, const Projection &proj,
156 QObject *parent) : Map(dirName, parent), _img(0), _tar(0), _ozf(0), _zoom(0),
157 _mapRatio(1.0), _valid(false)
159 CalibrationType type;
160 QString cf(calibrationFile(tar.files(), dirName, type));
162 if (type == MAP) {
163 QByteArray ba = tar.file(cf);
164 QBuffer buffer(&ba);
165 MapFile mf(buffer);
166 if (!mf.isValid()) {
167 _errorString = mf.errorString();
168 return;
171 _name = mf.name();
172 _map.size = mf.size();
173 _projection = mf.projection();
174 _transform = mf.transform();
175 } else if (type == GMI) {
176 QByteArray ba = tar.file(cf);
177 QBuffer buffer(&ba);
178 GmiFile gmi(buffer);
179 if (!gmi.isValid()) {
180 _errorString = gmi.errorString();
181 return;
184 _name = Util::file2name(cf);
185 _map.size = gmi.size();
186 _calibrationPoints = gmi.calibrationPoints();
187 _projection = proj;
188 computeTransform();
189 } else {
190 _errorString = "No calibration file found";
191 return;
194 QString tf(tarFile(dirName));
195 if (tf.isNull()) {
196 _errorString = "No map tar file found";
197 return;
199 _tar = new Tar(tf);
200 if (!_tar->open()) {
201 _errorString = _tar->fileName() + ": " + _tar->errorString();
202 return;
204 if (!setTileInfo(_tar->files())) {
205 _errorString = _tar->fileName() + ": " + _errorString;
206 return;
208 _tar->close();
210 _valid = true;
213 OziMap::~OziMap()
215 delete _img;
216 delete _tar;
217 delete _ozf;
220 bool OziMap::setImageInfo(const QString &path)
222 QFileInfo ii(_map.path);
224 if (ii.isRelative())
225 ii.setFile(path + "/" + _map.path);
227 if (!ii.exists()) {
228 int last = _map.path.lastIndexOf('\\');
229 if (last >= 0 && last < _map.path.length() - 1) {
230 QString fn(_map.path.mid(last + 1, _map.path.length() - last - 1));
231 ii.setFile(path + "/" + fn);
235 if (ii.exists())
236 _map.path = ii.absoluteFilePath();
237 else {
238 _errorString = QString("%1: No such image file").arg(_map.path);
239 return false;
242 if (OZF::isOZF(_map.path)) {
243 _ozf = new OZF(_map.path);
244 if (!_ozf->open()) {
245 _errorString = QString("%1: %2").arg(_map.path, _ozf->errorString());
246 return false;
248 _scale = _ozf->scale(_zoom);
249 _ozf->close();
250 } else {
251 QImageReader ir(_map.path);
252 if (!ir.canRead()) {
253 _errorString = QString("%1: Unsupported/invalid image file")
254 .arg(_map.path);
255 return false;
257 _map.size = ir.size();
260 return true;
263 bool OziMap::setTileInfo(const QStringList &tiles, const QString &path)
265 static const QRegularExpression rx("_[0-9]+_[0-9]+\\.");
267 if (!_map.size.isValid()) {
268 _errorString = "Missing total image size (IWH)";
269 return false;
272 for (int i = 0; i < tiles.size(); i++) {
273 const QString &tile = tiles.at(i);
275 if (tile.contains(rx)) {
276 QString pattern(QString(tile).replace(rx, "_%1_%2."));
278 if (_tar) {
279 QByteArray ba(_tar->file(tile));
280 QBuffer buffer(&ba);
281 _tile.path = pattern;
282 _tile.size = QImageReader(&buffer).size();
283 } else {
284 _tile.path = path + "/" + pattern;
285 _tile.size = QImageReader(path + "/" + tile).size();
288 if (_tile.size.isValid())
289 return true;
290 else
291 qWarning("%s: error reading tile image", qPrintable(tile));
295 _errorString = "Invalid/missing tile set";
296 return false;
299 void OziMap::load(const Projection &in, const Projection &out,
300 qreal deviceRatio, bool hidpi)
302 Q_UNUSED(out);
304 _mapRatio = hidpi ? deviceRatio : 1.0;
305 if (!_calibrationPoints.isEmpty()) {
306 _projection = in;
307 computeTransform();
310 if (_tar) {
311 Q_ASSERT(!_tar->isOpen());
312 if (!_tar->open()) {
313 qWarning("%s: %s", qPrintable(_tar->fileName()),
314 qPrintable(_tar->errorString()));
315 return;
318 if (_ozf) {
319 Q_ASSERT(!_ozf->isOpen());
320 if (!_ozf->open()) {
321 qWarning("%s: %s", qPrintable(_ozf->fileName()),
322 qPrintable(_ozf->errorString()));
323 return;
326 if (!_tile.isValid() && !_ozf) {
327 Q_ASSERT(!_img);
328 _img = new Image(_map.path);
329 _img->setDevicePixelRatio(_mapRatio);
333 void OziMap::unload()
335 delete _img;
336 _img = 0;
338 if (_tar && _tar->isOpen())
339 _tar->close();
341 if (_ozf && _ozf->isOpen())
342 _ozf->close();
345 void OziMap::drawTiled(QPainter *painter, const QRectF &rect) const
347 QSizeF ts(_tile.size.width() / _mapRatio, _tile.size.height() / _mapRatio);
348 QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
349 floor(rect.top() / ts.height()) * ts.height());
351 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
352 for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
353 for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
354 int x = round(tl.x() * _mapRatio + i * _tile.size.width());
355 int y = round(tl.y() * _mapRatio + j * _tile.size.height());
357 QString tileName(_tile.path.arg(QString::number(x),
358 QString::number(y)));
359 QPixmap pixmap;
361 if (_tar) {
362 QString key = _tar->fileName() + "/" + tileName;
363 if (!QPixmapCache::find(key, &pixmap)) {
364 QByteArray ba = _tar->file(tileName);
365 pixmap = QPixmap::fromImage(QImage::fromData(ba));
366 if (!pixmap.isNull())
367 QPixmapCache::insert(key, pixmap);
369 } else
370 pixmap = QPixmap(tileName);
372 if (pixmap.isNull())
373 qWarning("%s: error loading tile image", qPrintable(tileName));
374 else {
375 pixmap.setDevicePixelRatio(_mapRatio);
376 QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
377 painter->drawPixmap(tp, pixmap);
383 void OziMap::drawOZF(QPainter *painter, const QRectF &rect) const
385 QSizeF ts(_ozf->tileSize().width() / _mapRatio, _ozf->tileSize().height()
386 / _mapRatio);
387 QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
388 floor(rect.top() / ts.height()) * ts.height());
390 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
391 for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
392 for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
393 int x = round(tl.x() * _mapRatio + i * _ozf->tileSize().width());
394 int y = round(tl.y() * _mapRatio + j * _ozf->tileSize().height());
396 QPixmap pixmap;
397 QString key(_ozf->fileName() + "/" + QString::number(_zoom) + "_"
398 + QString::number(x) + "_" + QString::number(y));
400 if (!QPixmapCache::find(key, &pixmap)) {
401 pixmap = _ozf->tile(_zoom, x, y);
402 if (!pixmap.isNull())
403 QPixmapCache::insert(key, pixmap);
406 if (pixmap.isNull())
407 qWarning("%s: error loading tile image", qPrintable(key));
408 else {
409 pixmap.setDevicePixelRatio(_mapRatio);
410 QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
411 painter->drawPixmap(tp, pixmap);
417 void OziMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
419 Q_UNUSED(flags);
421 if (_ozf)
422 drawOZF(painter, rect);
423 else if (_img)
424 _img->draw(painter, rect, flags);
425 else if (_tile.isValid())
426 drawTiled(painter, rect);
429 QPointF OziMap::ll2xy(const Coordinates &c)
431 QPointF p(_transform.proj2img(_projection.ll2xy(c)));
432 return _ozf
433 ? QPointF(p.x() * _scale.x(), p.y() * _scale.y()) / _mapRatio
434 : p / _mapRatio;
437 Coordinates OziMap::xy2ll(const QPointF &p)
439 return _ozf
440 ? _projection.xy2ll(_transform.img2proj(QPointF(p.x() / _scale.x(),
441 p.y() / _scale.y()) * _mapRatio))
442 : _projection.xy2ll(_transform.img2proj(p * _mapRatio));
445 QRectF OziMap::bounds()
447 return _ozf
448 ? QRectF(QPointF(0, 0), _ozf->size(_zoom) / _mapRatio)
449 : QRectF(QPointF(0, 0), _map.size / _mapRatio);
452 int OziMap::zoomFit(const QSize &size, const RectC &rect)
454 if (!_ozf)
455 return _zoom;
457 if (!rect.isValid())
458 rescale(0);
459 else {
460 RectD prect(rect, _projection);
461 QRectF sbr(_transform.proj2img(prect.topLeft()),
462 _transform.proj2img(prect.bottomRight()));
464 for (int i = 0; i < _ozf->zooms(); i++) {
465 rescale(i);
466 if (sbr.size().width() * _scale.x() <= size.width()
467 && sbr.size().height() * _scale.y() <= size.height())
468 break;
472 return _zoom;
475 int OziMap::zoomIn()
477 if (_ozf)
478 rescale(qMax(_zoom - 1, 0));
480 return _zoom;
483 int OziMap::zoomOut()
485 if (_ozf)
486 rescale(qMin(_zoom + 1, _ozf->zooms() - 1));
488 return _zoom;
491 void OziMap::rescale(int zoom)
493 _zoom = zoom;
494 _scale = _ozf->scale(zoom);
497 void OziMap::computeTransform()
499 QList<ReferencePoint> rp;
501 for (int i = 0; i < _calibrationPoints.size(); i++)
502 rp.append(_calibrationPoints.at(i).rp(_projection));
504 _transform = Transform(rp);
507 Map *OziMap::createTAR(const QString &path, const Projection &proj, bool *isDir)
509 if (isDir)
510 *isDir = false;
512 return new OziMap(path, Unknown, proj);
515 Map *OziMap::createMAP(const QString &path, const Projection &proj, bool *isDir)
517 if (isDir)
518 *isDir = false;
520 return new OziMap(path, MAP, proj);
523 Map *OziMap::createGMI(const QString &path, const Projection &proj, bool *isDir)
525 if (isDir)
526 *isDir = false;
528 return new OziMap(path, GMI, proj);