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

453 lines
10 KiB
C++
Raw Normal View History

#include <QtGlobal>
#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-01-08 23:47:45 +01:00
#include "mapfile.h"
#include "geotiff.h"
2017-03-21 09:27:44 +01:00
#include "offlinemap.h"
2018-01-08 23:47:45 +01:00
void OfflineMap::computeResolution()
{
2018-01-08 23:47:45 +01:00
Coordinates tl = xy2ll((bounds().topLeft()));
Coordinates br = xy2ll(bounds().bottomRight());
2018-01-08 23:47:45 +01:00
qreal ds = tl.distanceTo(br);
qreal ps = QLineF(bounds().topLeft(), bounds().bottomRight()).length();
2018-01-08 23:47:45 +01:00
_resolution = ds/ps;
}
bool OfflineMap::getImageInfo(const QString &path)
2017-03-20 10:05:07 +01:00
{
QFileInfo ii(_imgPath);
if (ii.isRelative())
ii.setFile(path + "/" + _imgPath);
if (!ii.exists()) {
int last = _imgPath.lastIndexOf('\\');
if (last >= 0 && last < _imgPath.length() - 1) {
QStringRef fn(&_imgPath, last + 1, _imgPath.length() - last - 1);
ii.setFile(path + "/" + fn.toString());
}
}
if (ii.exists())
_imgPath = ii.absoluteFilePath();
else {
_errorString = QString("%1: No such image file").arg(_imgPath);
return false;
}
if (OZF::isOZF(_imgPath)) {
2017-08-11 00:22:21 +02:00
if (!_ozf.load(_imgPath)) {
_errorString = QString("%1: Error loading OZF file")
.arg(QFileInfo(_imgPath).fileName());
return false;
}
2017-04-14 22:39:33 +02:00
} else {
QImageReader img(_imgPath);
_size = img.size();
2017-08-11 00:22:21 +02:00
if (!_size.isValid()) {
_errorString = QString("%1: Error reading map image")
.arg(QFileInfo(_imgPath).fileName());
return false;
}
2017-03-20 10:05:07 +01:00
}
2017-03-21 01:15:29 +01:00
return true;
}
2017-03-27 10:31:41 +02:00
bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path)
2017-03-21 01:15:29 +01:00
{
QRegExp rx("_[0-9]+_[0-9]+\\.");
for (int i = 0; i < tiles.size(); i++) {
if (tiles.at(i).contains(rx)) {
_tileName = QString(tiles.at(i)).replace(rx, "_%1_%2.");
2017-03-27 02:41:30 +02:00
if (path.isNull()) {
QByteArray ba = _tar.file(tiles.at(i));
2017-03-21 20:51:23 +01:00
QBuffer buffer(&ba);
_tileSize = QImageReader(&buffer).size();
} else {
_tileName = path + "/" + _tileName;
2017-03-21 20:51:23 +01:00
_tileSize = QImageReader(path + "/" + tiles.at(i)).size();
}
2017-03-21 20:51:23 +01:00
if (!_tileSize.isValid()) {
_errorString = QString("Error retrieving tile size: "
"%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
return false;
}
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
}
2017-03-27 10:31:41 +02:00
bool OfflineMap::totalSizeSet()
{
if (!_size.isValid()) {
_errorString = "Missing total image size (IWH)";
2017-03-27 10:31:41 +02:00
return false;
} else
return true;
}
OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
: Map(parent)
{
QFileInfo fi(fileName);
QString suffix = fi.suffix().toLower();
_valid = false;
_img = 0;
2017-07-12 22:07:18 +02:00
_resolution = 0.0;
2017-08-11 00:22:21 +02:00
_zoom = 0;
_scale = QPointF(1.0, 1.0);
if (suffix == "tar") {
if (!_tar.load(fileName)) {
_errorString = "Error reading tar file";
return;
2017-03-21 01:15:29 +01:00
}
QString mapFileName = fi.completeBaseName() + ".map";
QByteArray ba = _tar.file(mapFileName);
if (ba.isNull()) {
_errorString = "Map file not found";
return;
}
QBuffer mapFile(&ba);
2018-01-08 23:47:45 +01:00
MapFile mf;
if (!mf.load(mapFile)) {
_errorString = mf.errorString();
return;
2018-01-08 23:47:45 +01:00
} else {
_name = mf.name();
_size = mf.size();
_imgPath = mf.image();
_projection = mf.projection();
_transform = mf.transform();
}
} else if (suffix == "map") {
MapFile mf;
QFile mapFile(fileName);
2018-01-08 23:47:45 +01:00
if (!mf.load(mapFile)) {
_errorString = mf.errorString();
return;
} else {
_name = mf.name();
_size = mf.size();
_imgPath = mf.image();
_projection = mf.projection();
_transform = mf.transform();
}
} else if (suffix == "tif" || suffix == "tiff") {
GeoTIFF gt;
if (!gt.load(fileName)) {
_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-01-08 23:47:45 +01:00
_imgPath = fileName;
_projection = gt.projection();
_transform = gt.transform();
}
} else {
_errorString = "Not a map file";
return;
}
2017-03-21 01:15:29 +01:00
if (_tar.isOpen()) {
2017-03-27 10:31:41 +02:00
if (!totalSizeSet())
return;
if (!getTileInfo(_tar.files()))
2017-03-20 10:05:07 +01:00
return;
_imgPath = QString();
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()) {
2017-03-27 10:31:41 +02:00
if (!totalSizeSet())
return;
if (!getTileInfo(set.entryList(), set.absolutePath()))
2017-03-21 01:15:29 +01:00
return;
2017-03-27 02:41:30 +02:00
_imgPath = QString();
2017-03-21 01:15:29 +01:00
} else {
if (!getImageInfo(fi.absolutePath()))
2017-03-21 01:15:29 +01:00
return;
2017-03-20 10:05:07 +01:00
}
}
2018-01-09 01:29:20 +01:00
_inverted = _transform.inverted();
computeResolution();
_valid = true;
}
OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
2017-03-27 02:41:30 +02:00
: Map(parent)
{
QFileInfo fi(fileName);
2018-01-08 23:47:45 +01:00
MapFile mf;
2017-03-27 02:41:30 +02:00
_valid = false;
_img = 0;
2017-07-12 22:07:18 +02:00
_resolution = 0.0;
2017-08-11 00:22:21 +02:00
_zoom = 0;
_scale = QPointF(1.0, 1.0);
2017-03-27 02:41:30 +02:00
QFileInfo map(fi.absolutePath());
QFileInfo layer(map.absolutePath());
QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
+ fi.fileName();
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-01-08 23:47:45 +01:00
if (!mf.load(buffer)) {
_errorString = mf.errorString();
2017-03-27 10:31:41 +02:00
return;
2018-01-08 23:47:45 +01:00
}
_name = mf.name();
_size = mf.size();
_projection = mf.projection();
_transform = mf.transform();
2017-03-27 02:41:30 +02:00
2018-01-08 23:47:45 +01:00
_inverted = _transform.inverted();
computeResolution();
2017-03-27 02:41:30 +02:00
_tarPath = 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;
}
2017-03-21 09:27:44 +01:00
void OfflineMap::load()
{
2017-03-27 02:41:30 +02:00
if (!_tarPath.isNull() && !_tileSize.isValid()) {
if (!_tar.load(_tarPath)) {
qWarning("%s: error loading tar file", qPrintable(_tarPath));
2017-03-27 02:41:30 +02:00
return;
}
2017-04-23 13:37:17 +02:00
if (!getTileInfo(_tar.files()))
qWarning("%s: %s", qPrintable(_tarPath), qPrintable(_errorString));
2017-03-20 10:05:07 +01:00
return;
2017-03-27 02:41:30 +02:00
}
2017-04-14 22:39:33 +02:00
if (!_img && !_imgPath.isNull() && !_ozf.isOpen()) {
2017-03-27 02:41:30 +02:00
_img = new QImage(_imgPath);
if (_img->isNull())
qWarning("%s: error loading map image", qPrintable(_imgPath));
}
}
2017-03-21 09:27:44 +01:00
void OfflineMap::unload()
{
2018-01-08 23:47:45 +01:00
delete _img;
_img = 0;
}
2017-04-15 08:59:31 +02:00
void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect)
{
2017-04-15 08:59:31 +02:00
QPoint tl = QPoint((int)floor(rect.left() / (qreal)_tileSize.width())
* _tileSize.width(), (int)floor(rect.top() / _tileSize.height())
* _tileSize.height());
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
for (int i = 0; i < ceil(s.width() / _tileSize.width()); i++) {
for (int j = 0; j < ceil(s.height() / _tileSize.height()); j++) {
int x = tl.x() + i * _tileSize.width();
int y = tl.y() + j * _tileSize.height();
QString tileName(_tileName.arg(QString::number(x),
QString::number(y)));
QPixmap pixmap;
if (_tar.isOpen()) {
QString key = _tar.fileName() + "/" + tileName;
2017-04-14 22:39:33 +02:00
if (!QPixmapCache::find(key, &pixmap)) {
2017-04-15 08:59:31 +02:00
QByteArray ba = _tar.file(tileName);
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);
if (pixmap.isNull()) {
qWarning("%s: error loading tile image", qPrintable(
_tileName.arg(QString::number(x), QString::number(y))));
2017-09-15 00:07:09 +02:00
painter->fillRect(QRectF(QPoint(x, y), _tileSize),
_backgroundColor);
2017-04-15 08:59:31 +02:00
} else
2017-04-14 22:39:33 +02:00
painter->drawPixmap(QPoint(x, y), pixmap);
}
2017-04-15 08:59:31 +02:00
}
}
void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect)
{
QPoint tl = QPoint((int)floor(rect.left() / _ozf.tileSize().width())
* _ozf.tileSize().width(), (int)floor(rect.top()
/ _ozf.tileSize().height()) * _ozf.tileSize().height());
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
for (int i = 0; i < ceil(s.width() / _ozf.tileSize().width()); i++) {
for (int j = 0; j < ceil(s.height() / _ozf.tileSize().height()); j++) {
int x = tl.x() + i * _ozf.tileSize().width();
int y = tl.y() + j * _ozf.tileSize().height();
QPixmap pixmap;
2017-08-11 00:22:21 +02:00
QString key = _ozf.fileName() + "/" + QString::number(_zoom) + "_"
+ QString::number(x) + "_" + QString::number(y);
2017-04-15 08:59:31 +02:00
if (!QPixmapCache::find(key, &pixmap)) {
2017-08-11 00:22:21 +02:00
pixmap = _ozf.tile(_zoom, x, y);
2017-04-15 08:59:31 +02:00
if (!pixmap.isNull())
QPixmapCache::insert(key, pixmap);
}
if (pixmap.isNull()) {
qWarning("%s: error loading tile image", qPrintable(key));
2017-08-11 00:22:21 +02:00
painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()),
2017-09-15 00:07:09 +02:00
_backgroundColor);
2017-04-15 08:59:31 +02:00
} else
painter->drawPixmap(QPoint(x, y), pixmap);
2017-03-20 10:05:07 +01:00
}
}
}
2017-04-15 08:59:31 +02:00
void OfflineMap::drawImage(QPainter *painter, const QRectF &rect)
{
2018-01-08 23:47:45 +01:00
QRect r(rect.toRect());
painter->drawImage(r.left(), r.top(), *_img, r.left(), r.top(),
r.width(), r.height());
2017-04-15 08:59:31 +02:00
}
void OfflineMap::draw(QPainter *painter, const QRectF &rect)
{
if (_ozf.isOpen())
drawOZF(painter, rect);
2017-04-15 08:59:31 +02:00
else if (_tileSize.isValid())
drawTiled(painter, rect);
2018-01-08 23:47:45 +01:00
else if (_img && !_img->isNull())
drawImage(painter, rect);
2018-01-08 23:47:45 +01:00
else
painter->fillRect(rect, _backgroundColor);
2017-04-15 08:59:31 +02:00
}
2017-08-11 00:22:21 +02:00
QPointF OfflineMap::ll2xy(const Coordinates &c)
{
if (_ozf.isOpen()) {
QPointF p(_transform.map(_projection.ll2xy(c)));
2017-08-11 00:22:21 +02:00
return QPointF(p.x() * _scale.x(), p.y() * _scale.y());
} else
return _transform.map(_projection.ll2xy(c));
2017-08-11 00:22:21 +02:00
}
Coordinates OfflineMap::xy2ll(const QPointF &p)
{
if (_ozf.isOpen()) {
return _projection.xy2ll(_inverted.map(QPointF(p.x() / _scale.x(),
p.y() / _scale.y())));
2017-08-11 00:22:21 +02:00
} else
return _projection.xy2ll(_inverted.map(p));
2017-08-11 00:22:21 +02:00
}
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())))
2017-08-11 00:22:21 +02:00
.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());
}