Now using a strict horizontal map scale
[GPXSee.git] / src / map / offlinemap.cpp
blobb4732747a80ba199fad80df23638c60e612932fb
1 #include <QPainter>
2 #include <QFileInfo>
3 #include <QMap>
4 #include <QDir>
5 #include <QBuffer>
6 #include <QImage>
7 #include <QImageReader>
8 #include <QPixmapCache>
9 #include "common/coordinates.h"
10 #include "common/rectc.h"
11 #include "tar.h"
12 #include "ozf.h"
13 #include "mapfile.h"
14 #include "geotiff.h"
15 #include "offlinemap.h"
18 bool OfflineMap::setImageInfo(const QString &path)
20 QFileInfo ii(_map.path);
22 if (ii.isRelative())
23 ii.setFile(path + "/" + _map.path);
25 if (!ii.exists()) {
26 int last = _map.path.lastIndexOf('\\');
27 if (last >= 0 && last < _map.path.length() - 1) {
28 QStringRef fn(&_map.path, last + 1, _map.path.length() - last - 1);
29 ii.setFile(path + "/" + fn.toString());
33 if (ii.exists())
34 _map.path = ii.absoluteFilePath();
35 else {
36 _errorString = QString("%1: No such image file").arg(_map.path);
37 return false;
40 if (OZF::isOZF(_map.path)) {
41 _ozf = new OZF(_map.path);
42 if (!_ozf->open()) {
43 _errorString = QString("%1: Error loading OZF file")
44 .arg(_ozf->fileName());
45 return false;
47 _scale = _ozf->scale(_zoom);
48 } else {
49 QImageReader img(_map.path);
50 _map.size = img.size();
51 if (!_map.size.isValid()) {
52 _errorString = QString("%1: Error reading map image")
53 .arg(QFileInfo(_map.path).fileName());
54 return false;
58 return true;
61 bool OfflineMap::setTileInfo(const QStringList &tiles, const QString &path)
63 if (!_map.size.isValid()) {
64 _errorString = "Missing total image size (IWH)";
65 return false;
68 QRegExp rx("_[0-9]+_[0-9]+\\.");
69 for (int i = 0; i < tiles.size(); i++) {
70 if (tiles.at(i).contains(rx)) {
71 _tile.path = QString(tiles.at(i)).replace(rx, "_%1_%2.");
73 if (_tar) {
74 QByteArray ba = _tar->file(tiles.at(i));
75 QBuffer buffer(&ba);
76 _tile.size = QImageReader(&buffer).size();
77 } else {
78 _tile.path = path + "/" + _tile.path;
79 _tile.size = QImageReader(path + "/" + tiles.at(i)).size();
81 if (!_tile.size.isValid()) {
82 _errorString = QString("Error retrieving tile size: "
83 "%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
84 return false;
87 _map.path = QString();
88 return true;
92 _errorString = "Invalid/missing tile set";
93 return false;
96 OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
97 : Map(parent), _img(0), _tar(0), _ozf(0), _zoom(0), _valid(false)
99 QFileInfo fi(fileName);
100 QString suffix = fi.suffix().toLower();
103 if (suffix == "tar") {
104 _tar = new Tar(fileName);
105 if (!_tar->open()) {
106 _errorString = "Error reading tar file";
107 return;
110 QString mapFileName = fi.completeBaseName() + ".map";
111 QByteArray ba = _tar->file(mapFileName);
112 if (ba.isNull()) {
113 _errorString = "Map file not found";
114 return;
116 QBuffer buffer(&ba);
117 MapFile mf(buffer);
118 if (!mf.isValid()) {
119 _errorString = mf.errorString();
120 return;
121 } else {
122 _name = mf.name();
123 _map.size = mf.size();
124 _map.path = mf.image();
125 _projection = mf.projection();
126 _transform = mf.transform();
128 } else if (suffix == "map") {
129 QFile file(fileName);
130 MapFile mf(file);
131 if (!mf.isValid()) {
132 _errorString = mf.errorString();
133 return;
134 } else {
135 _name = mf.name();
136 _map.size = mf.size();
137 _map.path = mf.image();
138 _projection = mf.projection();
139 _transform = mf.transform();
141 } else if (suffix == "tif" || suffix == "tiff") {
142 GeoTIFF gt(fileName);
143 if (!gt.isValid()) {
144 _errorString = gt.errorString();
145 return;
146 } else {
147 _name = fi.fileName();
148 _map.path = fileName;
149 _projection = gt.projection();
150 _transform = gt.transform();
152 } else {
153 _errorString = "Not a map file";
154 return;
157 if (_tar) {
158 if (!setTileInfo(_tar->files()))
159 return;
160 } else {
161 QDir set(fi.absolutePath() + "/" + "set");
162 if (set.exists()) {
163 if (!setTileInfo(set.entryList(), set.absolutePath()))
164 return;
165 } else {
166 if (!setImageInfo(fi.absolutePath()))
167 return;
171 _valid = true;
174 OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
175 : Map(parent), _img(0), _tar(0), _ozf(0), _zoom(0), _valid(false)
177 QFileInfo fi(fileName);
178 QFileInfo map(fi.absolutePath());
179 QFileInfo layer(map.absolutePath());
180 QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
181 + fi.fileName();
183 QByteArray ba = tar.file(mapFile);
184 if (ba.isNull()) {
185 _errorString = "Map file not found";
186 return;
188 QBuffer buffer(&ba);
189 MapFile mf(buffer);
190 if (!mf.isValid()) {
191 _errorString = mf.errorString();
192 return;
195 _name = mf.name();
196 _map.size = mf.size();
197 _projection = mf.projection();
198 _transform = mf.transform();
199 _tar = new Tar(fi.absolutePath() + "/" + fi.completeBaseName() + ".tar");
201 _valid = true;
204 OfflineMap::~OfflineMap()
206 delete _img;
207 delete _tar;
208 delete _ozf;
211 void OfflineMap::load()
213 if (_tar && !_tar->isOpen()) {
214 if (!_tar->open()) {
215 qWarning("%s: error loading tar file",
216 qPrintable(_tar->fileName()));
217 return;
219 if (!setTileInfo(_tar->files()))
220 qWarning("%s: %s", qPrintable(_tar->fileName()),
221 qPrintable(_errorString));
222 return;
225 if (!_ozf && !_img && _map.isValid()) {
226 _img = new QImage(_map.path);
227 if (_img->isNull())
228 qWarning("%s: error loading map image", qPrintable(_map.path));
232 void OfflineMap::unload()
234 delete _img;
235 _img = 0;
238 void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect) const
240 QPoint tl = QPoint((int)floor(rect.left() / (qreal)_tile.size.width())
241 * _tile.size.width(), (int)floor(rect.top() / _tile.size.height())
242 * _tile.size.height());
244 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
245 for (int i = 0; i < ceil(s.width() / _tile.size.width()); i++) {
246 for (int j = 0; j < ceil(s.height() / _tile.size.height()); j++) {
247 int x = tl.x() + i * _tile.size.width();
248 int y = tl.y() + j * _tile.size.height();
250 QString tileName(_tile.path.arg(QString::number(x),
251 QString::number(y)));
252 QPixmap pixmap;
254 if (_tar) {
255 QString key = _tar->fileName() + "/" + tileName;
256 if (!QPixmapCache::find(key, &pixmap)) {
257 QByteArray ba = _tar->file(tileName);
258 pixmap = QPixmap::fromImage(QImage::fromData(ba));
259 if (!pixmap.isNull())
260 QPixmapCache::insert(key, pixmap);
262 } else
263 pixmap = QPixmap(tileName);
265 if (pixmap.isNull())
266 qWarning("%s: error loading tile image", qPrintable(
267 _tile.path.arg(QString::number(x), QString::number(y))));
268 else
269 painter->drawPixmap(QPoint(x, y), pixmap);
274 void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect) const
276 QPoint tl = QPoint((int)floor(rect.left() / _ozf->tileSize().width())
277 * _ozf->tileSize().width(), (int)floor(rect.top()
278 / _ozf->tileSize().height()) * _ozf->tileSize().height());
280 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
281 for (int i = 0; i < ceil(s.width() / _ozf->tileSize().width()); i++) {
282 for (int j = 0; j < ceil(s.height() / _ozf->tileSize().height()); j++) {
283 int x = tl.x() + i * _ozf->tileSize().width();
284 int y = tl.y() + j * _ozf->tileSize().height();
286 QPixmap pixmap;
287 QString key = _ozf->fileName() + "/" + QString::number(_zoom) + "_"
288 + QString::number(x) + "_" + QString::number(y);
289 if (!QPixmapCache::find(key, &pixmap)) {
290 pixmap = _ozf->tile(_zoom, x, y);
291 if (!pixmap.isNull())
292 QPixmapCache::insert(key, pixmap);
295 if (pixmap.isNull())
296 qWarning("%s: error loading tile image", qPrintable(key));
297 else
298 painter->drawPixmap(QPoint(x, y), pixmap);
303 void OfflineMap::drawImage(QPainter *painter, const QRectF &rect) const
305 QRect r(rect.toRect());
306 painter->drawImage(r.left(), r.top(), *_img, r.left(), r.top(),
307 r.width(), r.height());
310 void OfflineMap::draw(QPainter *painter, const QRectF &rect, bool block)
312 Q_UNUSED(block);
314 if (_ozf)
315 drawOZF(painter, rect);
316 else if (_tile.isValid())
317 drawTiled(painter, rect);
318 else if (_img && !_img->isNull())
319 drawImage(painter, rect);
322 QPointF OfflineMap::ll2xy(const Coordinates &c)
324 QPointF p(_transform.proj2img(_projection.ll2xy(c)));
325 return _ozf ? QPointF(p.x() * _scale.x(), p.y() * _scale.y()) : p;
328 Coordinates OfflineMap::xy2ll(const QPointF &p)
330 return _ozf
331 ? _projection.xy2ll(_transform.img2proj(QPointF(p.x() / _scale.x(),
332 p.y() / _scale.y())))
333 : _projection.xy2ll(_transform.img2proj(p));
336 QRectF OfflineMap::bounds() const
338 return _ozf
339 ? QRectF(QPointF(0, 0), _ozf->size(_zoom))
340 : QRectF(QPointF(0, 0), _map.size);
343 int OfflineMap::zoomFit(const QSize &size, const RectC &rect)
345 if (!_ozf)
346 return _zoom;
348 if (!rect.isValid())
349 rescale(0);
350 else {
351 QPointF tl(_transform.proj2img(_projection.ll2xy(rect.topLeft())));
352 QPointF br(_transform.proj2img(_projection.ll2xy(rect.bottomRight())));
353 QRect sbr(QRectF(tl, br).toRect().normalized());
355 for (int i = 0; i < _ozf->zooms(); i++) {
356 rescale(i);
357 if (sbr.size().width() * _scale.x() <= size.width()
358 && sbr.size().height() * _scale.y() <= size.height())
359 break;
363 return _zoom;
366 int OfflineMap::zoomIn()
368 if (_ozf)
369 rescale(qMax(_zoom - 1, 0));
371 return _zoom;
374 int OfflineMap::zoomOut()
376 if (_ozf)
377 rescale(qMin(_zoom + 1, _ozf->zooms() - 1));
379 return _zoom;
382 void OfflineMap::rescale(int zoom)
384 _zoom = zoom;
385 _scale = _ozf->scale(zoom);