1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 11:45:53 +01:00

Added support for OZF zoom levels

This commit is contained in:
Martin Tůma 2017-08-11 00:22:21 +02:00
parent c708fa35fd
commit 73f06e61f0
4 changed files with 240 additions and 94 deletions

View File

@ -20,6 +20,7 @@
#include "lambertconic.h"
#include "albersequal.h"
#include "ozf.h"
#include "rectc.h"
#include "offlinemap.h"
@ -343,17 +344,20 @@ 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;
}
}
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());
}

View File

@ -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

View File

@ -5,7 +5,6 @@
#define OZF2_MAGIC 0x7778
#define OZF3_MAGIC 0x7780
#define SEPARATOR 0x77777777
static const quint8 XKEY[] =
{
@ -39,21 +38,22 @@ template<class T> 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,21 +143,23 @@ 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))
if (!readValue(tableOffset))
return false;
if (!_file.seek(offset))
zooms = (_file.size() - tableOffset - sizeof(quint32)) / sizeof(quint32);
for (int i = 0; i < zooms - 2; i++) {
if (!_file.seek(tableOffset + i * sizeof(quint32)))
return false;
// tiles offset (zoom level 0)
if (!readValue(offset))
if (!readValue(headerOffset))
return false;
if (!_file.seek(offset))
if (!_file.seek(headerOffset))
return false;
if (!readValue(w))
@ -154,27 +171,31 @@ bool OZF::readTileTable()
if (!readValue(y))
return false;
_size = QSize(w, h);
_dim = QSize(x, y);
Zoom zoom;
zoom.size = QSize(w, h);
zoom.dim = QSize(x, y);
_palette = QVector<quint32>(256);
if (!read(&(_palette[0]), sizeof(quint32) * 256))
zoom.palette = QVector<quint32>(256);
if (!read(&(zoom.palette[0]), sizeof(quint32) * 256))
return false;
for (int i = 0; i < _palette.size(); i++) {
bgr0 = qFromLittleEndian(_palette.at(i));
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;
_palette[i] = 0xFF000000 | r << 16 | g << 8 | b;
zoom.palette[i] = 0xFF000000 | r << 16 | g << 8 | b;
}
_tiles = QVector<quint32>(_dim.width() * _dim.height() + 1);
for (int i = 0; i < _tiles.size(); i++)
if (!readValue(_tiles[i]))
zoom.tiles = QVector<quint32>(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);

View File

@ -4,6 +4,7 @@
#include <QString>
#include <QSize>
#include <QColor>
#include <QList>
#include <QVector>
#include <QFile>
#include <QPixmap>
@ -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<QRgb> palette;
QVector<quint32> tiles;
};
template<class T> 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<QRgb> _palette;
QVector<quint32> _tiles;
QList<Zoom> _zooms;
QFile _file;
};