Support all variants of TrekBuddy maps/atlases
[GPXSee.git] / src / map / ozimap.cpp
blob8d414308fef6946a0e5f41c442e6694b5cad21bf
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 QString OziMap::calibrationFile(const QStringList &files, const QString path,
21 CalibrationType &type)
23 for (int i = 0; i < files.size(); i++) {
24 QFileInfo fi(files.at(i));
25 QString suffix(fi.suffix().toLower());
27 if (path.endsWith(fi.path())) {
28 if (suffix == "map") {
29 type = MAP;
30 return files.at(i);
31 } else if (suffix == "gmi") {
32 type = GMI;
33 return files.at(i);
38 type = Unknown;
39 return QString();
42 OziMap::OziMap(const QString &fileName, QObject *parent)
43 : Map(fileName, parent), _img(0), _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0),
44 _hasProj(true), _valid(false)
46 QFileInfo fi(fileName);
47 QString suffix(fi.suffix().toLower());
49 if (suffix == "tar") {
50 CalibrationType type;
52 _tar = new Tar(fileName);
53 if (!_tar->open()) {
54 _errorString = "Error reading tar file";
55 return;
57 QStringList files(_tar->files());
58 QString cf(calibrationFile(files, ".", type));
60 if (type == GMI) {
61 QByteArray ba(_tar->file(cf));
62 QBuffer buffer(&ba);
63 GmiFile gmi(buffer);
64 if (!gmi.isValid()) {
65 _errorString = gmi.errorString();
66 return;
67 } else {
68 _name = Util::file2name(fileName);
69 _map.size = gmi.size();
70 _map.path = gmi.image();
71 _transform = gmi.transform();
72 _projection = Projection(GCS::WGS84());
73 _hasProj = false;
75 } else if (type == MAP) {
76 QByteArray ba(_tar->file(cf));
77 QBuffer buffer(&ba);
78 MapFile mf(buffer);
79 if (!mf.isValid()) {
80 _errorString = mf.errorString();
81 return;
82 } else {
83 _name = mf.name();
84 _map.size = mf.size();
85 _map.path = mf.image();
86 _projection = mf.projection();
87 _transform = mf.transform();
89 } else {
90 _errorString = "No calibration file found";
91 return;
94 if (!setTileInfo(files))
95 return;
97 _tar->close();
98 } else {
99 QFile file(fileName);
101 if (suffix == "map") {
102 MapFile mf(file);
103 if (!mf.isValid()) {
104 _errorString = mf.errorString();
105 return;
106 } else {
107 _name = mf.name();
108 _map.size = mf.size();
109 _map.path = mf.image();
110 _projection = mf.projection();
111 _transform = mf.transform();
113 } else if (suffix == "gmi") {
114 GmiFile gmi(file);
115 if (!gmi.isValid()) {
116 _errorString = gmi.errorString();
117 return;
118 } else {
119 _name = Util::file2name(fileName);
120 _map.size = gmi.size();
121 _map.path = gmi.image();
122 _transform = gmi.transform();
123 _projection = Projection(GCS::WGS84());
124 _hasProj = false;
126 } else {
127 _errorString = "Unknown file type";
128 return;
131 QDir set(fi.absolutePath() + "/" + "set");
132 if (set.exists()) {
133 if (!setTileInfo(set.entryList(), set.absolutePath()))
134 return;
135 } else {
136 if (!setImageInfo(fi.absolutePath()))
137 return;
141 _valid = true;
144 OziMap::OziMap(const QString &dirName, Tar &tar, QObject *parent)
145 : Map(dirName, parent), _img(0), _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0),
146 _hasProj(true), _valid(false)
148 CalibrationType type;
149 QString cf(calibrationFile(tar.files(), dirName, type));
151 if (type == MAP) {
152 QByteArray ba = tar.file(cf);
153 QBuffer buffer(&ba);
154 MapFile mf(buffer);
155 if (!mf.isValid()) {
156 _errorString = mf.errorString();
157 return;
160 _name = mf.name();
161 _map.size = mf.size();
162 _projection = mf.projection();
163 _transform = mf.transform();
164 } else if (type == GMI) {
165 QByteArray ba = tar.file(cf);
166 QBuffer buffer(&ba);
167 GmiFile gmi(buffer);
168 if (!gmi.isValid()) {
169 _errorString = gmi.errorString();
170 return;
173 _name = Util::file2name(cf);
174 _map.size = gmi.size();
175 _transform = gmi.transform();
176 _projection = Projection(GCS::WGS84());
177 _hasProj = false;
178 } else {
179 _errorString = "No calibration file found";
180 return;
183 QFileInfo fi(cf);
184 QDir dir(dirName);
185 _tar = new Tar(dir.absoluteFilePath(fi.completeBaseName() + ".tar"));
187 if (!_tar->open()) {
188 _errorString = _tar->fileName() + ": error reading tar file";
189 return;
191 if (!setTileInfo(_tar->files())) {
192 _errorString = _tar->fileName() + ": " + _errorString;
193 return;
195 _tar->close();
197 _valid = true;
200 OziMap::~OziMap()
202 delete _img;
203 delete _tar;
204 delete _ozf;
207 bool OziMap::setImageInfo(const QString &path)
209 QFileInfo ii(_map.path);
211 if (ii.isRelative())
212 ii.setFile(path + "/" + _map.path);
214 if (!ii.exists()) {
215 int last = _map.path.lastIndexOf('\\');
216 if (last >= 0 && last < _map.path.length() - 1) {
217 QString fn(_map.path.mid(last + 1, _map.path.length() - last - 1));
218 ii.setFile(path + "/" + fn);
222 if (ii.exists())
223 _map.path = ii.absoluteFilePath();
224 else {
225 _errorString = QString("%1: No such image file").arg(_map.path);
226 return false;
229 if (OZF::isOZF(_map.path)) {
230 _ozf = new OZF(_map.path);
231 if (!_ozf || !_ozf->open()) {
232 _errorString = QString("%1: Error loading OZF file").arg(_map.path);
233 return false;
235 _scale = _ozf->scale(_zoom);
236 _ozf->close();
237 } else {
238 QImageReader ir(_map.path);
239 if (!ir.canRead()) {
240 _errorString = QString("%1: Unsupported/invalid image file")
241 .arg(_map.path);
242 return false;
244 _map.size = ir.size();
247 return true;
250 bool OziMap::setTileInfo(const QStringList &tiles, const QString &path)
252 if (!_map.size.isValid()) {
253 _errorString = "Missing total image size (IWH)";
254 return false;
257 QRegularExpression rx("_[0-9]+_[0-9]+\\.");
258 for (int i = 0; i < tiles.size(); i++) {
259 if (tiles.at(i).contains(rx)) {
260 _tile.path = QString(tiles.at(i)).replace(rx, "_%1_%2.");
262 if (_tar) {
263 QByteArray ba = _tar->file(tiles.at(i));
264 QBuffer buffer(&ba);
265 _tile.size = QImageReader(&buffer).size();
266 } else {
267 _tile.path = path + "/" + _tile.path;
268 _tile.size = QImageReader(path + "/" + tiles.at(i)).size();
270 if (!_tile.size.isValid()) {
271 _errorString = QString("Error retrieving tile size: "
272 "%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
273 return false;
276 _map.path = QString();
277 return true;
281 _errorString = "Invalid/missing tile set";
282 return false;
285 void OziMap::load(const Projection &in, const Projection &out,
286 qreal deviceRatio, bool hidpi)
288 Q_UNUSED(out);
290 _mapRatio = hidpi ? deviceRatio : 1.0;
291 if (!_hasProj)
292 _projection = in;
294 if (_tar) {
295 Q_ASSERT(!_tar->isOpen());
296 if (!_tar->open())
297 return;
299 if (_ozf) {
300 Q_ASSERT(!_ozf->isOpen());
301 if (!_ozf->open())
302 return;
304 if (!_tile.isValid() && !_ozf) {
305 Q_ASSERT(!_img);
306 _img = new Image(_map.path);
307 if (_img)
308 _img->setDevicePixelRatio(_mapRatio);
312 void OziMap::unload()
314 delete _img;
315 _img = 0;
317 if (_tar && _tar->isOpen())
318 _tar->close();
320 if (_ozf && _ozf->isOpen())
321 _ozf->close();
324 void OziMap::drawTiled(QPainter *painter, const QRectF &rect) const
326 QSizeF ts(_tile.size.width() / _mapRatio, _tile.size.height() / _mapRatio);
327 QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
328 floor(rect.top() / ts.height()) * ts.height());
330 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
331 for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
332 for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
333 int x = round(tl.x() * _mapRatio + i * _tile.size.width());
334 int y = round(tl.y() * _mapRatio + j * _tile.size.height());
336 QString tileName(_tile.path.arg(QString::number(x),
337 QString::number(y)));
338 QPixmap pixmap;
340 if (_tar) {
341 QString key = _tar->fileName() + "/" + tileName;
342 if (!QPixmapCache::find(key, &pixmap)) {
343 QByteArray ba = _tar->file(tileName);
344 pixmap = QPixmap::fromImage(QImage::fromData(ba));
345 if (!pixmap.isNull())
346 QPixmapCache::insert(key, pixmap);
348 } else
349 pixmap = QPixmap(tileName);
351 if (pixmap.isNull())
352 qWarning("%s: error loading tile image", qPrintable(
353 _tile.path.arg(QString::number(x), QString::number(y))));
354 else {
355 pixmap.setDevicePixelRatio(_mapRatio);
356 QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
357 painter->drawPixmap(tp, pixmap);
363 void OziMap::drawOZF(QPainter *painter, const QRectF &rect) const
365 QSizeF ts(_ozf->tileSize().width() / _mapRatio, _ozf->tileSize().height()
366 / _mapRatio);
367 QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
368 floor(rect.top() / ts.height()) * ts.height());
370 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
371 for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
372 for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
373 int x = round(tl.x() * _mapRatio + i * _ozf->tileSize().width());
374 int y = round(tl.y() * _mapRatio + j * _ozf->tileSize().height());
376 QPixmap pixmap;
377 QString key = _ozf->fileName() + "/" + QString::number(_zoom) + "_"
378 + QString::number(x) + "_" + QString::number(y);
379 if (!QPixmapCache::find(key, &pixmap)) {
380 pixmap = _ozf->tile(_zoom, x, y);
381 if (!pixmap.isNull())
382 QPixmapCache::insert(key, pixmap);
385 if (pixmap.isNull())
386 qWarning("%s: error loading tile image", qPrintable(key));
387 else {
388 pixmap.setDevicePixelRatio(_mapRatio);
389 QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
390 painter->drawPixmap(tp, pixmap);
396 void OziMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
398 Q_UNUSED(flags);
400 if (_ozf)
401 drawOZF(painter, rect);
402 else if (_img)
403 _img->draw(painter, rect, flags);
404 else if (_tile.isValid())
405 drawTiled(painter, rect);
408 QPointF OziMap::ll2xy(const Coordinates &c)
410 QPointF p(_transform.proj2img(_projection.ll2xy(c)));
411 return _ozf
412 ? QPointF(p.x() * _scale.x(), p.y() * _scale.y()) / _mapRatio
413 : p / _mapRatio;
416 Coordinates OziMap::xy2ll(const QPointF &p)
418 return _ozf
419 ? _projection.xy2ll(_transform.img2proj(QPointF(p.x() / _scale.x(),
420 p.y() / _scale.y()) * _mapRatio))
421 : _projection.xy2ll(_transform.img2proj(p * _mapRatio));
424 QRectF OziMap::bounds()
426 return _ozf
427 ? QRectF(QPointF(0, 0), _ozf->size(_zoom) / _mapRatio)
428 : QRectF(QPointF(0, 0), _map.size / _mapRatio);
431 int OziMap::zoomFit(const QSize &size, const RectC &rect)
433 if (!_ozf)
434 return _zoom;
436 if (!rect.isValid())
437 rescale(0);
438 else {
439 RectD prect(rect, _projection);
440 QRectF sbr(_transform.proj2img(prect.topLeft()),
441 _transform.proj2img(prect.bottomRight()));
443 for (int i = 0; i < _ozf->zooms(); i++) {
444 rescale(i);
445 if (sbr.size().width() * _scale.x() <= size.width()
446 && sbr.size().height() * _scale.y() <= size.height())
447 break;
451 return _zoom;
454 int OziMap::zoomIn()
456 if (_ozf)
457 rescale(qMax(_zoom - 1, 0));
459 return _zoom;
462 int OziMap::zoomOut()
464 if (_ozf)
465 rescale(qMin(_zoom + 1, _ozf->zooms() - 1));
467 return _zoom;
470 void OziMap::rescale(int zoom)
472 _zoom = zoom;
473 _scale = _ozf->scale(zoom);
476 Map *OziMap::createTAR(const QString &path, bool *isDir)
478 if (isDir)
479 *isDir = false;
481 return new OziMap(path);
484 Map *OziMap::createMAP(const QString &path, bool *isDir)
486 if (isDir)
487 *isDir = false;
489 return new OziMap(path);