Map API refactoring
[GPXSee.git] / src / map / encmap.cpp
blobbd92e76615f5a985ba1d104c99a8658916e6da62
1 #include <QPainter>
2 #include <QPixmapCache>
3 #include "common/range.h"
4 #include "common/wgs84.h"
5 #include "rectd.h"
6 #include "pcs.h"
7 #include "encmap.h"
9 #define TILE_SIZE 512
10 #define TEXT_EXTENT 160
12 using namespace ENC;
15 ENCMap::ENCMap(const QString &fileName, QObject *parent)
16 : Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)),
17 _tileRatio(1.0), _zoom(0)
19 if (_data.isValid()) {
20 _llBounds = _data.bounds();
21 updateTransform();
25 void ENCMap::load(const Projection &in, const Projection &out,
26 qreal deviceRatio, bool hidpi)
28 Q_UNUSED(in);
29 Q_UNUSED(hidpi);
31 _tileRatio = deviceRatio;
32 _projection = out;
33 _data.load();
34 QPixmapCache::clear();
37 void ENCMap::unload()
39 cancelJobs(true);
40 _data.clear();
43 int ENCMap::zoomFit(const QSize &size, const RectC &rect)
45 if (rect.isValid()) {
46 RectD pr(rect, _projection, 10);
48 _zoom = _data.zooms().min();
49 for (int i = _data.zooms().min() + 1; i <= _data.zooms().max(); i++) {
50 Transform t(transform(i));
51 QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
52 if (size.width() < r.width() || size.height() < r.height())
53 break;
54 _zoom = i;
56 } else
57 _zoom = _data.zooms().max();
59 updateTransform();
61 return _zoom;
64 int ENCMap::zoomIn()
66 cancelJobs(false);
68 _zoom = qMin(_zoom + 1, _data.zooms().max());
69 updateTransform();
70 return _zoom;
73 int ENCMap::zoomOut()
75 cancelJobs(false);
77 _zoom = qMax(_zoom - 1, _data.zooms().min());
78 updateTransform();
79 return _zoom;
82 void ENCMap::setZoom(int zoom)
84 _zoom = zoom;
85 updateTransform();
88 Transform ENCMap::transform(int zoom) const
90 int z = zoom + Util::log2i(TILE_SIZE);
92 double scale = _projection.isGeographic()
93 ? 360.0 / (1<<z) : (2.0 * M_PI * WGS84_RADIUS) / (1<<z);
94 PointD topLeft(_projection.ll2xy(_llBounds.topLeft()));
95 return Transform(ReferencePoint(PointD(0, 0), topLeft),
96 PointD(scale, scale));
99 void ENCMap::updateTransform()
101 _transform = transform(_zoom);
103 RectD prect(_llBounds, _projection);
104 _bounds = QRectF(_transform.proj2img(prect.topLeft()),
105 _transform.proj2img(prect.bottomRight()));
108 bool ENCMap::isRunning(int zoom, const QPoint &xy) const
110 for (int i = 0; i < _jobs.size(); i++) {
111 const QList<ENC::RasterTile> &tiles = _jobs.at(i)->tiles();
112 for (int j = 0; j < tiles.size(); j++) {
113 const ENC::RasterTile &mt = tiles.at(j);
114 if (mt.zoom() == zoom && mt.xy() == xy)
115 return true;
119 return false;
122 void ENCMap::runJob(ENCMapJob *job)
124 _jobs.append(job);
126 connect(job, &ENCMapJob::finished, this, &ENCMap::jobFinished);
127 job->run();
130 void ENCMap::removeJob(ENCMapJob *job)
132 _jobs.removeOne(job);
133 job->deleteLater();
136 void ENCMap::jobFinished(ENCMapJob *job)
138 const QList<ENC::RasterTile> &tiles = job->tiles();
140 for (int i = 0; i < tiles.size(); i++) {
141 const ENC::RasterTile &mt = tiles.at(i);
142 if (mt.isValid())
143 QPixmapCache::insert(key(mt.zoom(), mt.xy()), mt.pixmap());
146 removeJob(job);
148 emit tilesLoaded();
151 void ENCMap::cancelJobs(bool wait)
153 for (int i = 0; i < _jobs.size(); i++)
154 _jobs.at(i)->cancel(wait);
157 QString ENCMap::key(int zoom, const QPoint &xy) const
159 return path() + "-" + QString::number(zoom) + "_"
160 + QString::number(xy.x()) + "_" + QString::number(xy.y());
163 void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
165 Q_UNUSED(flags);
166 QPointF tl(floor(rect.left() / TILE_SIZE) * TILE_SIZE,
167 floor(rect.top() / TILE_SIZE) * TILE_SIZE);
168 QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
169 int width = ceil(s.width() / TILE_SIZE);
170 int height = ceil(s.height() / TILE_SIZE);
172 QList<RasterTile> tiles;
174 for (int i = 0; i < width; i++) {
175 for (int j = 0; j < height; j++) {
176 QPoint ttl(tl.x() + i * TILE_SIZE, tl.y() + j * TILE_SIZE);
177 if (isRunning(_zoom, ttl))
178 continue;
180 QPixmap pm;
181 if (QPixmapCache::find(key(_zoom, ttl), &pm))
182 painter->drawPixmap(ttl, pm);
183 else {
184 QList<MapData::Poly*> polygons;
185 QList<MapData::Line*> lines;
186 QList<MapData::Point*> points;
188 QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
189 ttl.y() + TILE_SIZE));
190 polyRect &= _bounds;
191 RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
192 _transform.img2proj(polyRect.bottomRight()));
193 RectC polyRectC(polyRectD.toRectC(_projection, 20));
194 _data.lines(polyRectC, &lines);
195 _data.polygons(polyRectC, &polygons);
197 QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
198 ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
199 + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
200 pointRect &= _bounds;
201 RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
202 _transform.img2proj(pointRect.bottomRight()));
203 _data.points(pointRectD.toRectC(_projection, 20), &points);
205 tiles.append(RasterTile(_projection, _transform, _data.zooms(),
206 _zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio,
207 lines, polygons, points));
212 if (!tiles.isEmpty()) {
213 if (flags & Map::Block) {
214 QFuture<void> future = QtConcurrent::map(tiles, &RasterTile::render);
215 future.waitForFinished();
217 for (int i = 0; i < tiles.size(); i++) {
218 const RasterTile &mt = tiles.at(i);
219 const QPixmap &pm = mt.pixmap();
220 painter->drawPixmap(mt.xy(), pm);
221 QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm);
223 } else
224 runJob(new ENCMapJob(tiles));
228 Map *ENCMap::create(const QString &path, bool *isMap)
230 if (isMap)
231 *isMap = false;
233 return new ENCMap(path);