diff --git a/src/offlinemap.cpp b/src/offlinemap.cpp index e26cb23e..4e2c4823 100644 --- a/src/offlinemap.cpp +++ b/src/offlinemap.cpp @@ -20,6 +20,7 @@ #include "lambertconic.h" #include "albersequal.h" #include "ozf.h" +#include "rectc.h" #include "offlinemap.h" @@ -343,16 +344,19 @@ bool OfflineMap::getImageInfo(const QString &path) } if (OZF::isOZF(_imgPath)) { - _ozf.load(_imgPath); - _size = _ozf.size(); + if (!_ozf.load(_imgPath)) { + _errorString = QString("%1: Error loading OZF file") + .arg(QFileInfo(_imgPath).fileName()); + return false; + } } else { QImageReader img(_imgPath); _size = img.size(); - } - if (!_size.isValid()) { - _errorString = QString("%1: Error reading map image") - .arg(QFileInfo(_imgPath).fileName()); - return false; + if (!_size.isValid()) { + _errorString = QString("%1: Error reading map image") + .arg(QFileInfo(_imgPath).fileName()); + return false; + } } return true; @@ -410,6 +414,8 @@ OfflineMap::OfflineMap(const QString &fileName, QObject *parent) _img = 0; _projection = 0; _resolution = 0.0; + _zoom = 0; + _scale = QPointF(1.0, 1.0); if (suffix == "tar") { if (!_tar.load(fileName)) { @@ -476,6 +482,8 @@ OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent) _img = 0; _projection = 0; _resolution = 0.0; + _zoom = 0; + _scale = QPointF(1.0, 1.0); QFileInfo map(fi.absolutePath()); QFileInfo layer(map.absolutePath()); @@ -551,7 +559,7 @@ void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect) int x = tl.x() + i * _tileSize.width(); int y = tl.y() + j * _tileSize.height(); - if (!QRectF(QPointF(x, y), _ozf.tileSize()).intersects(bounds())) { + if (!QRectF(QPointF(x, y), _tileSize).intersects(bounds())) { painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white); continue; } @@ -594,22 +602,24 @@ void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect) int y = tl.y() + j * _ozf.tileSize().height(); if (!QRectF(QPointF(x, y), _ozf.tileSize()).intersects(bounds())) { - painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white); + painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()), + Qt::white); continue; } QPixmap pixmap; - QString key = _ozf.fileName() + "/" + QString::number(x) - + "_" + QString::number(y); + QString key = _ozf.fileName() + "/" + QString::number(_zoom) + "_" + + QString::number(x) + "_" + QString::number(y); if (!QPixmapCache::find(key, &pixmap)) { - pixmap = _ozf.tile(x, y); + pixmap = _ozf.tile(_zoom, x, y); if (!pixmap.isNull()) QPixmapCache::insert(key, pixmap); } if (pixmap.isNull()) { qWarning("%s: error loading tile image", qPrintable(key)); - painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white); + painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()), + Qt::white); } else painter->drawPixmap(QPoint(x, y), pixmap); } @@ -636,3 +646,101 @@ void OfflineMap::draw(QPainter *painter, const QRectF &rect) else drawImage(painter, rect); } + +QPointF OfflineMap::ll2xy(const Coordinates &c) +{ + if (_ozf.isOpen()) { + QPointF p(_transform.map(_projection->ll2xy(c))); + return QPointF(p.x() * _scale.x(), p.y() * _scale.y()); + } else + return _transform.map(_projection->ll2xy(c)); +} + +Coordinates OfflineMap::xy2ll(const QPointF &p) +{ + if (_ozf.isOpen()) { + return _projection->xy2ll(_inverted.map(QPointF(p.x() / _scale.x(), + p.y() / _scale.y()))); + } else + return _projection->xy2ll(_inverted.map(p)); +} + +QRectF OfflineMap::bounds() const +{ + if (_ozf.isOpen()) + return QRectF(QPointF(0, 0), _ozf.size(_zoom)); + else + return QRectF(QPointF(0, 0), _size); +} + +qreal OfflineMap::resolution(const QPointF &p) const +{ + Q_UNUSED(p); + + if (_ozf.isOpen()) + return _resolution / ((_scale.x() + _scale.y()) / 2.0); + else + return _resolution; +} + +qreal OfflineMap::zoomFit(const QSize &size, const RectC &br) +{ + if (_ozf.isOpen()) { + if (!br.isValid()) + rescale(0); + else { + QRect sbr(QRectF(_transform.map(_projection->ll2xy(br.topLeft())), + _transform.map(_projection->ll2xy(br.bottomRight()))) + .toRect().normalized()); + + for (int i = 0; i < _ozf.zooms(); i++) { + rescale(i); + if (sbr.size().width() * _scale.x() <= size.width() + && sbr.size().height() * _scale.y() <= size.height()) + break; + } + } + } + + return _zoom; +} + +qreal OfflineMap::zoomFit(qreal resolution, const Coordinates &c) +{ + Q_UNUSED(c); + + if (_ozf.isOpen()) { + for (int i = 0; i < _ozf.zooms(); i++) { + rescale(i); + qreal sr = _resolution / ((_scale.x() + _scale.y()) / 2.0); + if (sr >= resolution) + break; + } + } + + return _zoom; +} + +qreal OfflineMap::zoomIn() +{ + if (_ozf.isOpen()) + rescale(qMax(_zoom - 1, 0)); + + return _zoom; +} + +qreal OfflineMap::zoomOut() +{ + if (_ozf.isOpen()) + rescale(qMin(_zoom + 1, _ozf.zooms() - 1)); + + return _zoom; +} + +void OfflineMap::rescale(int zoom) +{ + _zoom = zoom; + _scale = QPointF( + (qreal)_ozf.size(_zoom).width() / (qreal)_ozf.size(0).width(), + (qreal)_ozf.size(_zoom).height() / (qreal)_ozf.size(0).height()); +} diff --git a/src/offlinemap.h b/src/offlinemap.h index e3180051..7e2d74fa 100644 --- a/src/offlinemap.h +++ b/src/offlinemap.h @@ -23,19 +23,17 @@ public: const QString &name() const {return _name;} - QRectF bounds() const {return QRectF(QPointF(0, 0), _size);} - qreal resolution(const QPointF &) const {return _resolution;} + QRectF bounds() const; + qreal resolution(const QPointF &p) const; - qreal zoom() const {return 0;} - qreal zoomFit(const QSize &, const RectC &) {return 0;} - qreal zoomFit(qreal, const Coordinates &) {return 0;} - qreal zoomIn() {return 0;} - qreal zoomOut() {return 0;} + qreal zoom() const {return _zoom;} + qreal zoomFit(const QSize &size, const RectC &br); + qreal zoomFit(qreal resolution, const Coordinates &c); + qreal zoomIn(); + qreal zoomOut(); - QPointF ll2xy(const Coordinates &c) - {return _transform.map(_projection->ll2xy(c));} - Coordinates xy2ll(const QPointF &p) - {return _projection->xy2ll(_inverted.map(p));} + QPointF ll2xy(const Coordinates &c); + Coordinates xy2ll(const QPointF &p); void draw(QPainter *painter, const QRectF &rect); @@ -86,6 +84,8 @@ private: void drawOZF(QPainter *painter, const QRectF &rect); void drawImage(QPainter *painter, const QRectF &rect); + void rescale(int zoom); + QString _name; bool _valid; QString _errorString; @@ -102,6 +102,9 @@ private: QString _imgPath; QSize _tileSize; QString _tileName; + + int _zoom; + QPointF _scale; }; #endif // OFFLINEMAP_H diff --git a/src/ozf.cpp b/src/ozf.cpp index 5b405ba0..ff67db0d 100644 --- a/src/ozf.cpp +++ b/src/ozf.cpp @@ -5,7 +5,6 @@ #define OZF2_MAGIC 0x7778 #define OZF3_MAGIC 0x7780 -#define SEPARATOR 0x77777777 static const quint8 XKEY[] = { @@ -39,21 +38,22 @@ template bool OZF::readValue(T &val) return true; } -bool OZF::read(void *data, size_t size) +bool OZF::read(void *data, size_t size, size_t decryptSize) { if (_file.read((char*)data, size) < (qint64)size) return false; if (_decrypt) - decrypt(data, size, _key); + decrypt(data, decryptSize ? qMin(decryptSize, size) : size, _key); return true; } -bool OZF::readKey() +bool OZF::initOZF3() { quint8 randomNumber, initial; quint32 keyblock; + quint8 h1[8]; if (!_file.seek(14)) @@ -66,7 +66,15 @@ bool OZF::readKey() if (!readValue(initial)) return false; - _decrypt = true; _key = initial; + _decrypt = true; + _key = initial; + + if (!_file.seek(0)) + return false; + if (!read(h1, sizeof(h1))) + return false; + _tileSize = *(h1 + 6); + if (!_file.seek(15 + randomNumber)) return false; if (!readValue(keyblock)) @@ -104,21 +112,28 @@ bool OZF::readKey() return true; } +bool OZF::initOZF2() +{ + if (!_file.seek(6)) + return false; + if (!readValue(_tileSize)) + return false; + + return true; +} + bool OZF::readHeaders() { quint16 magic; - quint32 separator; if (!readValue(magic)) return false; if (magic == OZF2_MAGIC) { - if (!_file.seek(_file.pos() + 52)) - return false; - if (!readValue(separator) || separator != SEPARATOR) + if (!initOZF2()) return false; } else if (magic == OZF3_MAGIC) { - if (!readKey()) + if (!initOZF3()) return false; } else return false; @@ -128,52 +143,58 @@ bool OZF::readHeaders() bool OZF::readTileTable() { - quint32 offset, bgr0, w, h; + quint32 tableOffset, headerOffset, bgr0, w, h; quint16 x, y; + int zooms; - if (!_file.seek(_file.size() - sizeof(offset))) + if (!_file.seek(_file.size() - sizeof(tableOffset))) return false; - // table offset - if (!readValue(offset)) - return false; - if (!_file.seek(offset)) - return false; - // tiles offset (zoom level 0) - if (!readValue(offset)) - return false; - if (!_file.seek(offset)) + if (!readValue(tableOffset)) return false; + zooms = (_file.size() - tableOffset - sizeof(quint32)) / sizeof(quint32); - if (!readValue(w)) - return false; - if (!readValue(h)) - return false; - if (!readValue(x)) - return false; - if (!readValue(y)) - return false; - - _size = QSize(w, h); - _dim = QSize(x, y); - - _palette = QVector(256); - if (!read(&(_palette[0]), sizeof(quint32) * 256)) - return false; - for (int i = 0; i < _palette.size(); i++) { - bgr0 = qFromLittleEndian(_palette.at(i)); - - quint32 b = (bgr0 & 0x000000FF); - quint32 g = (bgr0 & 0x0000FF00) >> 8; - quint32 r = (bgr0 & 0x00FF0000) >> 16; - - _palette[i] = 0xFF000000 | r << 16 | g << 8 | b; - } - - _tiles = QVector(_dim.width() * _dim.height() + 1); - for (int i = 0; i < _tiles.size(); i++) - if (!readValue(_tiles[i])) + for (int i = 0; i < zooms - 2; i++) { + if (!_file.seek(tableOffset + i * sizeof(quint32))) return false; + if (!readValue(headerOffset)) + return false; + if (!_file.seek(headerOffset)) + return false; + + if (!readValue(w)) + return false; + if (!readValue(h)) + return false; + if (!readValue(x)) + return false; + if (!readValue(y)) + return false; + + Zoom zoom; + zoom.size = QSize(w, h); + zoom.dim = QSize(x, y); + + zoom.palette = QVector(256); + if (!read(&(zoom.palette[0]), sizeof(quint32) * 256)) + return false; + for (int i = 0; i < zoom.palette.size(); i++) { + bgr0 = qFromLittleEndian(zoom.palette.at(i)); + + quint32 b = (bgr0 & 0x000000FF); + quint32 g = (bgr0 & 0x0000FF00) >> 8; + quint32 r = (bgr0 & 0x00FF0000) >> 16; + + zoom.palette[i] = 0xFF000000 | r << 16 | g << 8 | b; + } + + zoom.tiles = QVector(zoom.dim.width() * zoom.dim.height() + 1); + for (int i = 0; i < zoom.tiles.size(); i++) + if (!readValue(zoom.tiles[i])) + return false; + + _zooms.append(zoom); + } return true; } @@ -196,23 +217,25 @@ bool OZF::load(const QString &path) if (!readTileTable()) { qWarning("%s: file format error", qPrintable(_file.fileName())); _file.close(); - _size = QSize(); return false; } return true; } -QPixmap OZF::tile(int x, int y) +QPixmap OZF::tile(int zoom, int x, int y) { Q_ASSERT(_file.isOpen()); + Q_ASSERT(0 <= zoom && zoom < _zooms.count()); - int i = (y/tileSize().height()) * _dim.width() + (x/tileSize().width()); - if (i >= _tiles.size() - 1 || i < 0) + const Zoom &z = _zooms.at(zoom); + + int i = (y/tileSize().height()) * z.dim.width() + (x/tileSize().width()); + if (i >= z.tiles.size() - 1 || i < 0) return QPixmap(); - int size = _tiles.at(i+1) - _tiles.at(i); - if (!_file.seek(_tiles.at(i))) + int size = z.tiles.at(i+1) - z.tiles.at(i); + if (!_file.seek(z.tiles.at(i))) return QPixmap(); quint32 bes = qToBigEndian(tileSize().width() * tileSize().height()); @@ -220,21 +243,27 @@ QPixmap OZF::tile(int x, int y) ba.resize(sizeof(bes) + size); *(ba.data()) = bes; - if (_file.read(ba.data() + sizeof(bes), size) != size) + if (!read(ba.data() + sizeof(bes), size, 16)) return QPixmap(); - if (_decrypt) - decrypt(ba.data() + sizeof(bes), qMin(16, size), _key); QByteArray uba = qUncompress(ba); if (uba.size() != tileSize().width() * tileSize().height()) return QPixmap(); QImage img((const uchar*)uba.constData(), tileSize().width(), tileSize().height(), QImage::Format_Indexed8); - img.setColorTable(_palette); + img.setColorTable(z.palette); return QPixmap::fromImage(img.mirrored()); } +QSize OZF::size(int zoom) const +{ + Q_ASSERT(_file.isOpen()); + Q_ASSERT(0 <= zoom && zoom < _zooms.count()); + + return _zooms.at(zoom).size; +} + bool OZF::isOZF(const QString &path) { QFile file(path); diff --git a/src/ozf.h b/src/ozf.h index f995df4e..02880d1b 100644 --- a/src/ozf.h +++ b/src/ozf.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,27 +19,32 @@ public: QString fileName() const {return _file.fileName();} bool isOpen() const {return _file.isOpen();} - QSize size() const {return _size;} - QSize tileSize() const {return QSize(64, 64);} - QPixmap tile(int x, int y); + int zooms() const {return _zooms.size();} + QSize size(int zoom) const; + QSize tileSize() const {return QSize(_tileSize, _tileSize);} + QPixmap tile(int zoom, int x, int y); static bool isOZF(const QString &path); private: + struct Zoom { + QSize size; + QSize dim; + QVector palette; + QVector tiles; + }; + template bool readValue(T &val); - bool read(void *data, size_t size); - bool readKey(); + bool read(void *data, size_t size, size_t decryptSize = 0); + bool initOZF3(); + bool initOZF2(); bool readHeaders(); bool readTileTable(); + quint16 _tileSize; bool _decrypt; quint8 _key; - - QSize _size; - QSize _dim; - QVector _palette; - QVector _tiles; - + QList _zooms; QFile _file; };