1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/map/offlinemap.cpp

405 lines
9.5 KiB
C++
Raw Normal View History

#include <QPainter>
#include <QFileInfo>
#include <QMap>
#include <QDir>
2017-03-21 01:15:29 +01:00
#include <QBuffer>
#include <QImage>
2017-03-21 20:51:23 +01:00
#include <QImageReader>
#include <QPixmapCache>
2017-11-26 18:54:03 +01:00
#include "common/coordinates.h"
#include "common/rectc.h"
2018-03-08 02:24:10 +01:00
#include "tar.h"
#include "ozf.h"
2018-01-08 23:47:45 +01:00
#include "mapfile.h"
#include "geotiff.h"
2018-08-18 21:06:36 +02:00
#include "config.h"
2017-03-21 09:27:44 +01:00
#include "offlinemap.h"
2018-03-08 19:08:39 +01:00
bool OfflineMap::setImageInfo(const QString &path)
2017-03-20 10:05:07 +01:00
{
2018-03-08 02:24:10 +01:00
QFileInfo ii(_map.path);
if (ii.isRelative())
2018-03-08 02:24:10 +01:00
ii.setFile(path + "/" + _map.path);
if (!ii.exists()) {
2018-03-08 02:24:10 +01:00
int last = _map.path.lastIndexOf('\\');
if (last >= 0 && last < _map.path.length() - 1) {
QStringRef fn(&_map.path, last + 1, _map.path.length() - last - 1);
ii.setFile(path + "/" + fn.toString());
}
}
if (ii.exists())
2018-03-08 02:24:10 +01:00
_map.path = ii.absoluteFilePath();
else {
2018-03-08 02:24:10 +01:00
_errorString = QString("%1: No such image file").arg(_map.path);
return false;
}
2018-03-08 02:24:10 +01:00
if (OZF::isOZF(_map.path)) {
_ozf = new OZF(_map.path);
if (!_ozf->open()) {
2017-08-11 00:22:21 +02:00
_errorString = QString("%1: Error loading OZF file")
2018-03-08 02:24:10 +01:00
.arg(_ozf->fileName());
2017-08-11 00:22:21 +02:00
return false;
}
2018-03-08 02:24:10 +01:00
_scale = _ozf->scale(_zoom);
2017-04-14 22:39:33 +02:00
} else {
2018-03-08 02:24:10 +01:00
QImageReader img(_map.path);
_map.size = img.size();
if (!_map.size.isValid()) {
2017-08-11 00:22:21 +02:00
_errorString = QString("%1: Error reading map image")
2018-03-08 02:24:10 +01:00
.arg(QFileInfo(_map.path).fileName());
2017-08-11 00:22:21 +02:00
return false;
}
2017-03-20 10:05:07 +01:00
}
2017-03-21 01:15:29 +01:00
return true;
}
2018-03-08 19:08:39 +01:00
bool OfflineMap::setTileInfo(const QStringList &tiles, const QString &path)
2017-03-21 01:15:29 +01:00
{
2018-03-08 19:08:39 +01:00
if (!_map.size.isValid()) {
_errorString = "Missing total image size (IWH)";
return false;
}
QRegExp rx("_[0-9]+_[0-9]+\\.");
for (int i = 0; i < tiles.size(); i++) {
if (tiles.at(i).contains(rx)) {
2018-03-08 02:24:10 +01:00
_tile.path = QString(tiles.at(i)).replace(rx, "_%1_%2.");
2018-03-08 02:24:10 +01:00
if (_tar) {
QByteArray ba = _tar->file(tiles.at(i));
2017-03-21 20:51:23 +01:00
QBuffer buffer(&ba);
2018-03-08 02:24:10 +01:00
_tile.size = QImageReader(&buffer).size();
} else {
2018-03-08 02:24:10 +01:00
_tile.path = path + "/" + _tile.path;
_tile.size = QImageReader(path + "/" + tiles.at(i)).size();
}
2018-03-08 02:24:10 +01:00
if (!_tile.size.isValid()) {
_errorString = QString("Error retrieving tile size: "
"%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
return false;
}
2018-03-08 19:08:39 +01:00
_map.path = QString();
return true;
}
2017-03-20 10:05:07 +01:00
}
2017-03-21 01:15:29 +01:00
2017-04-23 13:37:17 +02:00
_errorString = "Invalid/missing tile set";
return false;
2017-03-20 10:05:07 +01:00
}
OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
2018-08-18 21:06:36 +02:00
: Map(parent), _img(0), _tar(0), _ozf(0), _zoom(0), _ratio(1.0), _valid(false)
{
QFileInfo fi(fileName);
QString suffix = fi.suffix().toLower();
if (suffix == "tar") {
2018-03-08 02:24:10 +01:00
_tar = new Tar(fileName);
if (!_tar->open()) {
_errorString = "Error reading tar file";
return;
2017-03-21 01:15:29 +01:00
}
QString mapFileName = fi.completeBaseName() + ".map";
2018-03-08 02:24:10 +01:00
QByteArray ba = _tar->file(mapFileName);
if (ba.isNull()) {
_errorString = "Map file not found";
return;
}
2018-03-21 19:09:37 +01:00
QBuffer buffer(&ba);
MapFile mf(buffer);
if (!mf.isValid()) {
2018-01-08 23:47:45 +01:00
_errorString = mf.errorString();
return;
2018-01-08 23:47:45 +01:00
} else {
_name = mf.name();
2018-03-08 02:24:10 +01:00
_map.size = mf.size();
_map.path = mf.image();
2018-01-08 23:47:45 +01:00
_projection = mf.projection();
_transform = mf.transform();
}
} else if (suffix == "map") {
2018-03-21 19:09:37 +01:00
QFile file(fileName);
MapFile mf(file);
if (!mf.isValid()) {
2018-01-08 23:47:45 +01:00
_errorString = mf.errorString();
return;
} else {
_name = mf.name();
2018-03-08 02:24:10 +01:00
_map.size = mf.size();
_map.path = mf.image();
2018-01-08 23:47:45 +01:00
_projection = mf.projection();
_transform = mf.transform();
}
} else if (suffix == "tif" || suffix == "tiff") {
2018-03-22 20:00:30 +01:00
GeoTIFF gt(fileName);
if (!gt.isValid()) {
2018-01-08 23:47:45 +01:00
_errorString = gt.errorString();
return;
2018-01-08 23:47:45 +01:00
} else {
2018-01-09 01:29:20 +01:00
_name = fi.fileName();
2018-03-08 02:24:10 +01:00
_map.path = fileName;
2018-01-08 23:47:45 +01:00
_projection = gt.projection();
_transform = gt.transform();
}
} else {
_errorString = "Not a map file";
return;
}
2018-03-08 02:24:10 +01:00
if (_tar) {
2018-03-08 19:08:39 +01:00
if (!setTileInfo(_tar->files()))
2017-03-20 10:05:07 +01:00
return;
2017-03-21 01:15:29 +01:00
} else {
QDir set(fi.absolutePath() + "/" + "set");
2017-03-21 01:15:29 +01:00
if (set.exists()) {
2018-03-08 19:08:39 +01:00
if (!setTileInfo(set.entryList(), set.absolutePath()))
2017-03-27 10:31:41 +02:00
return;
2017-03-21 01:15:29 +01:00
} else {
2018-03-08 19:08:39 +01:00
if (!setImageInfo(fi.absolutePath()))
2017-03-21 01:15:29 +01:00
return;
2017-03-20 10:05:07 +01:00
}
}
_valid = true;
}
OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
2018-08-18 21:06:36 +02:00
: Map(parent), _img(0), _tar(0), _ozf(0), _zoom(0), _ratio(1.0), _valid(false)
2017-03-27 02:41:30 +02:00
{
QFileInfo fi(fileName);
QFileInfo map(fi.absolutePath());
QFileInfo layer(map.absolutePath());
QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
+ fi.fileName();
2018-03-21 19:09:37 +01:00
QByteArray ba = tar.file(mapFile);
if (ba.isNull()) {
_errorString = "Map file not found";
2017-03-27 02:41:30 +02:00
return;
}
QBuffer buffer(&ba);
2018-03-21 19:09:37 +01:00
MapFile mf(buffer);
if (!mf.isValid()) {
2018-01-08 23:47:45 +01:00
_errorString = mf.errorString();
2017-03-27 10:31:41 +02:00
return;
2018-01-08 23:47:45 +01:00
}
_name = mf.name();
2018-03-08 02:24:10 +01:00
_map.size = mf.size();
2018-01-08 23:47:45 +01:00
_projection = mf.projection();
_transform = mf.transform();
2018-03-08 02:24:10 +01:00
_tar = new Tar(fi.absolutePath() + "/" + fi.completeBaseName() + ".tar");
2017-03-27 02:41:30 +02:00
_valid = true;
}
OfflineMap::~OfflineMap()
{
2018-01-08 23:47:45 +01:00
delete _img;
2018-03-08 02:24:10 +01:00
delete _tar;
delete _ozf;
}
2017-03-21 09:27:44 +01:00
void OfflineMap::load()
{
2018-03-08 02:24:10 +01:00
if (_tar && !_tar->isOpen()) {
if (!_tar->open()) {
qWarning("%s: error loading tar file",
qPrintable(_tar->fileName()));
2017-03-27 02:41:30 +02:00
return;
}
2018-03-08 19:08:39 +01:00
if (!setTileInfo(_tar->files()))
2018-03-08 02:24:10 +01:00
qWarning("%s: %s", qPrintable(_tar->fileName()),
qPrintable(_errorString));
2017-03-20 10:05:07 +01:00
return;
2017-03-27 02:41:30 +02:00
}
2018-03-08 02:24:10 +01:00
if (!_ozf && !_img && _map.isValid()) {
_img = new QImage(_map.path);
2018-08-18 21:06:36 +02:00
if (!_img || _img->isNull()) {
2018-03-08 02:24:10 +01:00
qWarning("%s: error loading map image", qPrintable(_map.path));
2018-08-18 21:06:36 +02:00
return;
}
#ifdef ENABLE_HIDPI
_img->setDevicePixelRatio(_ratio);
#endif // ENABLE_HIDPI
2017-03-27 02:41:30 +02:00
}
}
2017-03-21 09:27:44 +01:00
void OfflineMap::unload()
{
2018-01-08 23:47:45 +01:00
delete _img;
_img = 0;
}
2018-03-08 19:08:39 +01:00
void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect) const
{
2018-08-18 21:06:36 +02:00
QSizeF ts(_tile.size.width() / _ratio, _tile.size.height() / _ratio);
QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
floor(rect.top() / ts.height()) * ts.height());
2017-04-15 08:59:31 +02:00
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
2018-08-18 21:06:36 +02:00
for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
int x = round(tl.x() * _ratio + i * _tile.size.width());
int y = round(tl.y() * _ratio + j * _tile.size.height());
2017-04-15 08:59:31 +02:00
2018-03-08 02:24:10 +01:00
QString tileName(_tile.path.arg(QString::number(x),
2017-04-15 08:59:31 +02:00
QString::number(y)));
QPixmap pixmap;
2018-03-08 02:24:10 +01:00
if (_tar) {
QString key = _tar->fileName() + "/" + tileName;
2017-04-14 22:39:33 +02:00
if (!QPixmapCache::find(key, &pixmap)) {
2018-03-08 02:24:10 +01:00
QByteArray ba = _tar->file(tileName);
2017-04-15 08:59:31 +02:00
pixmap = QPixmap::fromImage(QImage::fromData(ba));
2017-04-14 22:39:33 +02:00
if (!pixmap.isNull())
QPixmapCache::insert(key, pixmap);
}
2017-04-15 08:59:31 +02:00
} else
pixmap = QPixmap(tileName);
2018-05-22 22:40:15 +02:00
if (pixmap.isNull())
2017-04-15 08:59:31 +02:00
qWarning("%s: error loading tile image", qPrintable(
2018-03-08 02:24:10 +01:00
_tile.path.arg(QString::number(x), QString::number(y))));
2018-08-18 21:06:36 +02:00
else {
#ifdef ENABLE_HIDPI
pixmap.setDevicePixelRatio(_ratio);
#endif // ENABLE_HIDPI
QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
painter->drawPixmap(tp, pixmap);
}
2017-04-14 22:39:33 +02:00
}
2017-04-15 08:59:31 +02:00
}
}
2018-03-08 19:08:39 +01:00
void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect) const
2017-04-15 08:59:31 +02:00
{
2018-08-18 21:06:36 +02:00
QSizeF ts(_ozf->tileSize().width() / _ratio, _ozf->tileSize().height()
/ _ratio);
QPointF tl(floor(rect.left() / ts.width()) * ts.width(),
floor(rect.top() / ts.height()) * ts.height());
2017-04-15 08:59:31 +02:00
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
2018-08-18 21:06:36 +02:00
for (int i = 0; i < ceil(s.width() / ts.width()); i++) {
for (int j = 0; j < ceil(s.height() / ts.height()); j++) {
int x = round(tl.x() * _ratio + i * _ozf->tileSize().width());
int y = round(tl.y() * _ratio + j * _ozf->tileSize().height());
2017-04-15 08:59:31 +02:00
QPixmap pixmap;
2018-03-08 02:24:10 +01:00
QString key = _ozf->fileName() + "/" + QString::number(_zoom) + "_"
2017-08-11 00:22:21 +02:00
+ QString::number(x) + "_" + QString::number(y);
2017-04-15 08:59:31 +02:00
if (!QPixmapCache::find(key, &pixmap)) {
2018-03-08 02:24:10 +01:00
pixmap = _ozf->tile(_zoom, x, y);
2017-04-15 08:59:31 +02:00
if (!pixmap.isNull())
QPixmapCache::insert(key, pixmap);
}
2018-05-22 22:40:15 +02:00
if (pixmap.isNull())
2017-04-15 08:59:31 +02:00
qWarning("%s: error loading tile image", qPrintable(key));
2018-08-18 21:06:36 +02:00
else {
#ifdef ENABLE_HIDPI
pixmap.setDevicePixelRatio(_ratio);
#endif // ENABLE_HIDPI
QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
painter->drawPixmap(tp, pixmap);
}
2017-03-20 10:05:07 +01:00
}
}
}
2017-04-15 08:59:31 +02:00
2018-03-08 19:08:39 +01:00
void OfflineMap::drawImage(QPainter *painter, const QRectF &rect) const
2017-04-15 08:59:31 +02:00
{
2018-08-18 21:06:36 +02:00
painter->drawImage(rect.topLeft(), *_img, QRectF(rect.topLeft() * _ratio,
rect.size() * _ratio));
2017-04-15 08:59:31 +02:00
}
2018-04-28 16:07:32 +02:00
void OfflineMap::draw(QPainter *painter, const QRectF &rect, bool block)
2017-04-15 08:59:31 +02:00
{
2018-04-28 16:07:32 +02:00
Q_UNUSED(block);
2018-03-08 02:24:10 +01:00
if (_ozf)
drawOZF(painter, rect);
2018-03-08 02:24:10 +01:00
else if (_tile.isValid())
drawTiled(painter, rect);
2018-01-08 23:47:45 +01:00
else if (_img && !_img->isNull())
drawImage(painter, rect);
2017-04-15 08:59:31 +02:00
}
2017-08-11 00:22:21 +02:00
QPointF OfflineMap::ll2xy(const Coordinates &c)
2017-08-11 00:22:21 +02:00
{
2018-04-15 17:32:25 +02:00
QPointF p(_transform.proj2img(_projection.ll2xy(c)));
2018-08-18 21:06:36 +02:00
return _ozf
? QPointF(p.x() * _scale.x(), p.y() * _scale.y()) / _ratio
: p / _ratio;
2017-08-11 00:22:21 +02:00
}
Coordinates OfflineMap::xy2ll(const QPointF &p)
2017-08-11 00:22:21 +02:00
{
2018-03-22 20:00:30 +01:00
return _ozf
? _projection.xy2ll(_transform.img2proj(QPointF(p.x() / _scale.x(),
2018-08-18 21:06:36 +02:00
p.y() / _scale.y()) * _ratio))
: _projection.xy2ll(_transform.img2proj(p * _ratio));
2017-08-11 00:22:21 +02:00
}
2018-07-13 09:51:41 +02:00
QRectF OfflineMap::bounds()
2017-08-11 00:22:21 +02:00
{
2018-03-22 20:00:30 +01:00
return _ozf
2018-08-18 21:06:36 +02:00
? QRectF(QPointF(0, 0), _ozf->size(_zoom) / _ratio)
: QRectF(QPointF(0, 0), _map.size / _ratio);
2017-08-11 00:22:21 +02:00
}
2018-03-22 20:00:30 +01:00
int OfflineMap::zoomFit(const QSize &size, const RectC &rect)
2017-08-11 00:22:21 +02:00
{
2018-03-22 20:00:30 +01:00
if (!_ozf)
return _zoom;
if (!rect.isValid())
rescale(0);
else {
2018-04-15 17:32:25 +02:00
QPointF tl(_transform.proj2img(_projection.ll2xy(rect.topLeft())));
QPointF br(_transform.proj2img(_projection.ll2xy(rect.bottomRight())));
2018-03-22 20:00:30 +01:00
QRect sbr(QRectF(tl, br).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;
2017-08-11 00:22:21 +02:00
}
}
return _zoom;
}
int OfflineMap::zoomIn()
2017-08-11 00:22:21 +02:00
{
2018-03-08 02:24:10 +01:00
if (_ozf)
2017-08-11 00:22:21 +02:00
rescale(qMax(_zoom - 1, 0));
return _zoom;
}
int OfflineMap::zoomOut()
2017-08-11 00:22:21 +02:00
{
2018-03-08 02:24:10 +01:00
if (_ozf)
rescale(qMin(_zoom + 1, _ozf->zooms() - 1));
2017-08-11 00:22:21 +02:00
return _zoom;
}
void OfflineMap::rescale(int zoom)
{
_zoom = zoom;
2018-03-08 02:24:10 +01:00
_scale = _ozf->scale(zoom);
2017-08-11 00:22:21 +02:00
}