Header includes cleanup
[GPXSee.git] / src / map / jnxmap.cpp
blobf85da9a288bc2b37c3d321d1d52509d8014f9853
1 #include <QtEndian>
2 #include <QPainter>
3 #include <QFileInfo>
4 #include <QPixmapCache>
5 #include "rectd.h"
6 #include "jnxmap.h"
9 #define ic2dc(x) ((x) * 180.0 / 0x7FFFFFFF)
11 template<class T> bool JNXMap::readValue(T &val)
13 T data;
15 if (_file.read((char*)&data, sizeof(T)) < (qint64)sizeof(T))
16 return false;
18 val = qFromLittleEndian(data);
20 return true;
23 bool JNXMap::readString(QByteArray& ba)
25 char byte;
27 while (true) {
28 if (!_file.getChar(&byte))
29 return false;
30 else if (!byte)
31 return true;
32 else
33 ba += byte;
37 bool JNXMap::readHeader()
39 qint32 lat1, lon2, lat2, lon1;
40 quint32 version, dummy, levels;
42 if (!(readValue(version) && readValue(dummy) && readValue(lat1)
43 && readValue(lon2) && readValue(lat2) && readValue(lon1)
44 && readValue(levels)))
45 return false;
47 if (version < 3 || version > 4)
48 return false;
50 _bounds = RectC(Coordinates(ic2dc(lon1), ic2dc(lat1)),
51 Coordinates(ic2dc(lon2), ic2dc(lat2)));
52 if (!levels || levels > 32 || !_bounds.isValid())
53 return false;
55 if (!_file.seek(version > 3 ? 0x34 : 0x30))
56 return false;
58 QVector<Level> lh(levels);
59 for (int i = 0; i < lh.count(); i++) {
60 Level &l = lh[i];
62 if (!(readValue(l.count) && readValue(l.offset) && readValue(l.scale)))
63 return false;
64 if (version > 3) {
65 QByteArray ba;
66 if (!(readValue(dummy) && readString(ba)))
67 return false;
71 _zooms.reserve(lh.size());
72 for (int i = 0; i < lh.count(); i++)
73 _zooms.append(new Zoom(lh.at(i)));
75 return true;
78 bool JNXMap::readTiles()
80 for (int i = 0; i < _zooms.size(); i++) {
81 Zoom *z = _zooms[i];
83 if (!_file.seek(z->level.offset))
84 return false;
86 z->tiles = QVector<Tile>(z->level.count);
87 for (quint32 j = 0; j < z->level.count; j++) {
88 Tile &tile = z->tiles[j];
90 if (!(readValue(tile.top) && readValue(tile.right)
91 && readValue(tile.bottom) && readValue(tile.left)
92 && readValue(tile.width) && readValue(tile.height)
93 && readValue(tile.size) && readValue(tile.offset)))
94 return false;
96 RectC llrect(Coordinates(ic2dc(tile.left), ic2dc(tile.top)),
97 Coordinates(ic2dc(tile.right), ic2dc(tile.bottom)));
98 RectD rect(_projection.ll2xy(llrect.topLeft()),
99 _projection.ll2xy(llrect.bottomRight()));
101 if (j == 0) {
102 ReferencePoint tl(PointD(0, 0), rect.topLeft());
103 ReferencePoint br(PointD(tile.width, tile.height),
104 rect.bottomRight());
105 z->transform = Transform(tl, br);
108 QRectF trect(z->transform.proj2img(rect.topLeft()),
109 z->transform.proj2img(rect.bottomRight()));
110 tile.pos = trect.topLeft();
112 qreal min[2], max[2];
113 min[0] = trect.left();
114 min[1] = trect.top();
115 max[0] = trect.right();
116 max[1] = trect.bottom();
117 z->tree.Insert(min, max, &tile);
121 return true;
124 void JNXMap::clearTiles()
126 for (int i = 0; i < _zooms.size(); i++) {
127 Zoom *z = _zooms[i];
128 z->tiles.clear();
129 z->tree.RemoveAll();
133 JNXMap::JNXMap(const QString &fileName, QObject *parent)
134 : Map(fileName, parent), _file(fileName), _zoom(0), _mapRatio(1.0),
135 _valid(false)
137 if (!_file.open(QIODevice::ReadOnly)) {
138 _errorString = _file.errorString();
139 return;
142 if (!readHeader()) {
143 _errorString = "JNX file format error";
144 return;
147 _file.close();
149 _valid = true;
152 JNXMap::~JNXMap()
154 qDeleteAll(_zooms);
157 void JNXMap::load(const Projection &in, const Projection &out,
158 qreal deviceRatio, bool hidpi)
160 Q_UNUSED(out);
162 _projection = in;
163 _mapRatio = hidpi ? deviceRatio : 1.0;
165 if (_file.open(QIODevice::ReadOnly))
166 readTiles();
169 void JNXMap::unload()
171 _file.close();
172 clearTiles();
175 QPointF JNXMap::ll2xy(const Coordinates &c)
177 const Zoom *z = _zooms.at(_zoom);
178 return z->transform.proj2img(_projection.ll2xy(c)) / _mapRatio;
181 Coordinates JNXMap::xy2ll(const QPointF &p)
183 const Zoom *z = _zooms.at(_zoom);
184 return _projection.xy2ll(z->transform.img2proj(p * _mapRatio));
187 QRectF JNXMap::bounds()
189 return QRectF(ll2xy(_bounds.topLeft()), ll2xy(_bounds.bottomRight()));
192 int JNXMap::zoomFit(const QSize &size, const RectC &rect)
194 if (!rect.isValid())
195 _zoom = _zooms.size() - 1;
196 else {
197 for (int i = 1; i < _zooms.count(); i++) {
198 _zoom = i;
199 QRect sbr(QPoint(ll2xy(rect.topLeft()).toPoint()),
200 QPoint(ll2xy(rect.bottomRight()).toPoint()));
201 if (sbr.size().width() >= size.width() || sbr.size().height()
202 >= size.height()) {
203 _zoom--;
204 break;
209 return _zoom;
212 int JNXMap::zoomIn()
214 _zoom = qMin(_zoom + 1, _zooms.size() - 1);
215 return _zoom;
218 int JNXMap::zoomOut()
220 _zoom = qMax(_zoom - 1, 0);
221 return _zoom;
224 QPixmap JNXMap::pixmap(const Tile *tile, QFile *file)
226 QPixmap pm;
228 QString key = file->fileName() + "-" + QString::number(tile->offset);
229 if (!QPixmapCache::find(key, &pm)) {
230 QByteArray ba;
231 ba.resize(tile->size + 2);
232 ba[0] = (char)0xFF;
233 ba[1] = (char)0xD8;
234 char *data = ba.data() + 2;
236 if (!file->seek(tile->offset))
237 return QPixmap();
238 if (!file->read(data, tile->size))
239 return QPixmap();
240 pm = QPixmap::fromImage(QImage::fromData(ba));
242 if (!pm.isNull())
243 QPixmapCache::insert(key, pm);
246 return pm;
249 bool JNXMap::cb(Tile *tile, void *context)
251 Ctx *ctx = static_cast<Ctx*>(context);
252 QPixmap pm(pixmap(tile, ctx->file));
253 pm.setDevicePixelRatio(ctx->ratio);
254 ctx->painter->drawPixmap(tile->pos / ctx->ratio, pm);
256 return true;
259 void JNXMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
261 Q_UNUSED(flags);
262 const RTree<Tile*, qreal, 2> &tree = _zooms.at(_zoom)->tree;
263 Ctx ctx(painter, &_file, _mapRatio);
264 QRectF rr(rect.topLeft() * _mapRatio, rect.size() * _mapRatio);
266 qreal min[2], max[2];
267 min[0] = rr.left();
268 min[1] = rr.top();
269 max[0] = rr.right();
270 max[1] = rr.bottom();
271 tree.Search(min, max, cb, &ctx);
274 Map *JNXMap::create(const QString &path, const Projection &proj, bool *isDir)
276 Q_UNUSED(proj);
278 if (isDir)
279 *isDir = false;
281 return new JNXMap(path);